diff --git a/UnblockNeteaseMusic/Makefile b/UnblockNeteaseMusic/Makefile new file mode 100644 index 000000000..8ac09ff31 --- /dev/null +++ b/UnblockNeteaseMusic/Makefile @@ -0,0 +1,47 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=UnblockNeteaseMusic +PKG_VERSION:=0.28.0 +PKG_RELEASE:=1 + +PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz +PKG_SOURCE_URL:=https://codeload.github.com/UnblockNeteaseMusic/server/tar.gz/v${PKG_VERSION}? +PKG_HASH:=0a1e843ffaa7b8ce65cc221daec07b63754473f0430019acb228f5b254602318 + +PKG_SOURCE_SUBDIR:=$(PKG_NAME) +PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_SOURCE_SUBDIR) +PKG_BUILD_PARALLEL:=1 + +include $(INCLUDE_DIR)/package.mk + +define Package/UnblockNeteaseMusic + SECTION:=multimedia + CATEGORY:=Multimedia + TITLE:=Revive Netease Cloud Music (NodeJS) + URL:=https://github.com/nondanee/UnblockNeteaseMusic + DEPENDS:=+node + PKGARCH:=all +endef + +define Package/$(PKG_NAME)/description +Revive Netease Cloud Music (NodeJS) +endef + +define Build/Prepare + tar -xzvf $(DL_DIR)/$(PKG_SOURCE) -C $(PKG_BUILD_DIR) + mkdir -p $(PKG_BUILD_DIR)/$(PKG_NAME) + echo -e $(PKG_VERSION) > $(PKG_BUILD_DIR)/$(PKG_NAME)/core_ver +endef + +define Build/Configure +endef + +define Build/Compile +endef + +define Package/$(PKG_NAME)/install + $(INSTALL_DIR) $(1)/usr/share/$(PKG_NAME) + $(CP) $(PKG_BUILD_DIR)/$(PKG_NAME)/* $(1)/usr/share/$(PKG_NAME) +endef + +$(eval $(call BuildPackage,$(PKG_NAME))) diff --git a/adguardhome/Makefile b/adguardhome/Makefile new file mode 100644 index 000000000..1c00a8c17 --- /dev/null +++ b/adguardhome/Makefile @@ -0,0 +1,81 @@ +# +# This is free software, licensed under the GNU General Public License v2. +# See /LICENSE for more information. +# + +include $(TOPDIR)/rules.mk + +PKG_NAME:=adguardhome +PKG_VERSION:=0.107.71 +PKG_RELEASE:=1 + +PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz +PKG_SOURCE_URL:=https://codeload.github.com/AdguardTeam/AdGuardHome/tar.gz/v$(PKG_VERSION)? +PKG_HASH:=f3dde5da6ba48270ac25bd2f501c4ce1af54ddeef93fcd84ef3a8270cec9539f +PKG_BUILD_DIR:=$(BUILD_DIR)/AdGuardHome-$(PKG_VERSION) + +PKG_LICENSE:=GPL-3.0-only +PKG_LICENSE_FILES:=LICENSE.txt +PKG_MAINTAINER:=Dobroslaw Kijowski + +PKG_BUILD_DEPENDS:=golang/host +PKG_BUILD_PARALLEL:=1 +PKG_BUILD_FLAGS:=no-mips16 + +GO_PKG:=github.com/AdguardTeam/AdGuardHome +GO_PKG_BUILD_PKG:=$(GO_PKG) + +AGH_BUILD_TIME:=$(shell date -d @$(SOURCE_DATE_EPOCH) +%FT%TZ%z) +GO_PKG_LDFLAGS_X:= \ + $(GO_PKG)/internal/version.channel=release \ + $(GO_PKG)/internal/version.version=v$(PKG_VERSION) \ + $(GO_PKG)/internal/version.buildtime=$(AGH_BUILD_TIME) \ + $(GO_PKG)/internal/version.goarm=$(GO_ARM) \ + $(GO_PKG)/internal/version.gomips=$(GO_MIPS) + +include $(INCLUDE_DIR)/package.mk +include $(TOPDIR)/feeds/packages/lang/golang/golang-package.mk + +define Package/adguardhome + SECTION:=net + CATEGORY:=Network + TITLE:=Network-wide ads and trackers blocking DNS server + URL:=https://github.com/AdguardTeam/AdGuardHome + DEPENDS:=$(GO_ARCH_DEPENDS) +ca-bundle +endef + +define Package/adguardhome/conffiles +/etc/adguardhome.yaml +/etc/config/adguardhome +endef + +define Package/adguardhome/description + Free and open source, powerful network-wide ads and trackers blocking DNS server. +endef + +FRONTEND_FILE:=$(PKG_NAME)-frontend-$(PKG_VERSION).tar.gz +define Download/adguardhome-frontend + URL:=https://github.com/AdguardTeam/AdGuardHome/releases/download/v$(PKG_VERSION)/ + URL_FILE:=AdGuardHome_frontend.tar.gz + FILE:=$(FRONTEND_FILE) + HASH:=51b229a5dff010c17bd8894bbf4291907e93708c366801e32181be7f37dd4488 +endef + +define Build/Prepare + $(call Build/Prepare/Default) + + gzip -dc $(DL_DIR)/$(FRONTEND_FILE) | $(HOST_TAR) -C $(PKG_BUILD_DIR)/ $(TAR_OPTIONS) +endef + +define Package/adguardhome/install + $(call GoPackage/Package/Install/Bin,$(1)) + $(INSTALL_DIR) $(1)/etc/init.d + $(INSTALL_BIN) ./files/adguardhome.init $(1)/etc/init.d/adguardhome + + $(INSTALL_DIR) $(1)/etc/config + $(INSTALL_DATA) ./files/adguardhome.config $(1)/etc/config/adguardhome +endef + +$(eval $(call Download,adguardhome-frontend)) +$(eval $(call GoBinPackage,adguardhome)) +$(eval $(call BuildPackage,adguardhome)) diff --git a/adguardhome/files/adguardhome.config b/adguardhome/files/adguardhome.config new file mode 100644 index 000000000..a9558020b --- /dev/null +++ b/adguardhome/files/adguardhome.config @@ -0,0 +1,4 @@ +config adguardhome config + option enabled '0' + # Where to store persistent data by AdGuard Home + option workdir /var/adguardhome diff --git a/adguardhome/files/adguardhome.init b/adguardhome/files/adguardhome.init new file mode 100644 index 000000000..270353c3a --- /dev/null +++ b/adguardhome/files/adguardhome.init @@ -0,0 +1,25 @@ +#!/bin/sh /etc/rc.common + +PROG=/usr/bin/AdGuardHome + +USE_PROCD=1 + +# starts just after network starts to avoid some network race conditions +START=25 +# stops before networking stops +STOP=89 + +start_service() { + config_load adguardhome + config_get_bool enabled config enabled + config_get WORK_DIR config workdir + + [ "$enabled" -eq "1" ] || exit 2 + [ -d "$WORK_DIR" ] || mkdir -m 0755 -p "$WORK_DIR" + + procd_open_instance + procd_set_param command "$PROG" -c /etc/adguardhome.yaml -w "$WORK_DIR" --no-check-update + procd_set_param stdout 1 + procd_set_param stderr 1 + procd_close_instance +} diff --git a/alist/Makefile b/alist/Makefile index ac20c9558..3037b7bc2 100644 --- a/alist/Makefile +++ b/alist/Makefile @@ -7,13 +7,13 @@ include $(TOPDIR)/rules.mk PKG_NAME:=alist -PKG_VERSION:=3.45.0 -PKG_WEB_VERSION:=3.45.0 +PKG_VERSION:=3.55.0 +PKG_WEB_VERSION:=3.55.0 PKG_RELEASE:=1 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz PKG_SOURCE_URL:=https://codeload.github.com/AlistGo/alist/tar.gz/v$(PKG_VERSION)? -PKG_HASH:=224119ea5a3b43694e5342c460ab471d6477db1bf7ade5180d542a32363cb097 +PKG_HASH:=c9947567d4b2b19d63f3170ecab626a9c670a7d15965f88b14889a58e11900ab PKG_LICENSE:=GPL-3.0 PKG_LICENSE_FILE:=LICENSE @@ -23,7 +23,7 @@ define Download/$(PKG_NAME)-web FILE:=$(PKG_NAME)-web-$(PKG_WEB_VERSION).tar.gz URL_FILE:=dist.tar.gz URL:=https://github.com/AlistGo/alist-web/releases/download/$(PKG_WEB_VERSION)/ - HASH:=408b1822893ba6dd6bbeb4055d6c8b96c178d10f4fbb8e5696cf14dcc88dd2fb +HASH:=1857caa4e64c0c9c2d92daea5953e8c5f2a5576e33e799b172a51e974f16fd6c endef PKG_BUILD_DEPENDS:=golang/host @@ -31,7 +31,7 @@ PKG_BUILD_PARALLEL:=1 PKG_USE_MIPS16:=0 PKG_BUILD_FLAGS:=no-mips16 -GO_PKG:=github.com/alist-org/alist +GO_PKG:=github.com/AlistGo/alist GO_PKG_LDFLAGS:= \ -X '$(GO_PKG)/v3/internal/conf.BuiltAt=$(shell date '+%Y-%m-%d %H:%M:%S %z')' \ -X '$(GO_PKG)/v3/internal/conf.GoVersion=$(shell $(STAGING_DIR_HOSTPKG)/bin/go version | sed 's/go version //')' \ @@ -48,7 +48,7 @@ define Package/$(PKG_NAME) CATEGORY:=Network SUBMENU:=Web Servers/Proxies TITLE:=A file list program that supports multiple storage - URL:=http://www.alistgo.com + URL:=https://alist.nn.ci/ DEPENDS:=$(GO_ARCH_DEPENDS) +ca-bundle endef diff --git a/alist/files/alist.config b/alist/files/alist.config index 2ec1ed998..bca32f59c 100644 --- a/alist/files/alist.config +++ b/alist/files/alist.config @@ -29,4 +29,3 @@ config alist option cors_allow_methods '*' option cors_allow_headers '*' option s3 '0' - diff --git a/alist/files/alist.init b/alist/files/alist.init index b55af5c5b..dc98bc7f7 100755 --- a/alist/files/alist.init +++ b/alist/files/alist.init @@ -5,6 +5,7 @@ START=99 USE_PROCD=1 PROG=/usr/bin/alist +LOG_FILE=/var/log/alist.log get_config() { config_get_bool enabled $1 enabled 1 @@ -20,12 +21,10 @@ get_config() { config_get temp_dir $1 temp_dir "/tmp/alist" config_get token_expires_in $1 token_expires_in 48 config_get max_connections $1 max_connections 0 - config_get max_concurrency $1 max_concurrency 64 config_get tls_insecure_skip_verify $1 tls_insecure_skip_verify 1 # log config_get log $1 log 1 - config_get log_path $1 log_path '/var/log/alist.log' config_get log_max_size $1 log_max_size 10 config_get log_max_backups $1 log_max_backups 5 config_get log_max_age $1 log_max_age 28 @@ -68,21 +67,13 @@ get_config() { config_get s3_port $1 s3_port 5246 config_get s3_ssl $1 s3_ssl 0 - # ftp - config_get ftp $1 ftp 0 - config_get ftp_port $1 ftp_port 5221 - config_get find_pasv_port_attempts $1 find_pasv_port_attempts 50 - config_get active_transfer_port_non_20 $1 active_transfer_port_non_20 0 - config_get idle_timeout $1 idle_timeout 900 - config_get connection_timeout $1 connection_timeout 30 - config_get disable_active_mode $1 disable_active_mode 0 - config_get default_transfer_binary $1 default_transfer_binary 0 - config_get enable_active_conn_ip_check $1 enable_active_conn_ip_check 1 - config_get enable_pasv_conn_ip_check $1 enable_pasv_conn_ip_check 1 - - # sftp - config_get sftp $1 sftp 0 - config_get sftp_port $1 sftp_port 5222 + config_load network + config_get lan_addr lan ipaddr "0.0.0.0" + if echo "${lan_addr}" | grep -Fq ' '; then + lan_addr="0.0.0.0" + else + lan_addr=${lan_addr%%/*} + fi # init jwt_secret [ -z "$jwt_secret" ] && jwt_secret=$(tr -cd "a-zA-Z0-9" < "/dev/urandom" | head -c16) @@ -98,7 +89,7 @@ set_firewall() { uci set firewall.alist.target="ACCEPT" uci set firewall.alist.src="wan" uci set firewall.alist.proto="tcp" - uci set firewall.alist.dest_port="$port $ftp_port $sftp_port" + uci set firewall.alist.dest_port="$port" uci set firewall.alist.enabled="1" uci commit firewall /etc/init.d/firewall reload >/dev/null 2>&1 @@ -112,10 +103,6 @@ set_firewall() { start_service() { config_load alist config_foreach get_config alist - - # Remove firewall rules when stopping alist process via uci - external_access="deny" set_firewall - [ $enabled -ne 1 ] && return 1 mkdir -p "$temp_dir" "$data_dir" [ "$ssl" -eq 1 ] && https_port=$port http_port="-1" || https_port="-1" http_port=$port @@ -124,11 +111,16 @@ start_service() { else delayed_start=$delayed_start fi - - [ "$allow_wan" -eq "1" ] && external_access="allow" || external_access="deny" + if [ "$allow_wan" -eq "1" ]; then + listen_addr="0.0.0.0" + external_access="allow" + else + listen_addr=$lan_addr + external_access="deny" + fi set_firewall - > "$log_path" + > $LOG_FILE # init config json_init @@ -161,7 +153,7 @@ start_service() { # scheme json_add_object "scheme" - json_add_string "address" "0.0.0.0" + json_add_string "address" "$listen_addr" json_add_int "http_port" "$http_port" json_add_int "https_port" "$https_port" json_add_boolean "force_https" "$force_https" @@ -178,7 +170,7 @@ start_service() { # log json_add_object "log" json_add_boolean "enable" "$log" - json_add_string "name" "$log_path" + json_add_string "name" "$LOG_FILE" json_add_int "max_size" "$log_max_size" json_add_int "max_backups" "$log_max_backups" json_add_int "max_age" "$log_max_age" @@ -187,7 +179,6 @@ start_service() { json_add_int "delayed_start" "$delayed_start" json_add_int "max_connections" "$max_connections" - json_add_int "max_concurrency" "$max_concurrency" json_add_boolean "tls_insecure_skip_verify" "$tls_insecure_skip_verify" # tasks @@ -230,26 +221,6 @@ start_service() { json_add_boolean "ssl" "$s3_ssl" json_close_object - # ftp - json_add_object "ftp" - json_add_boolean "enable" "$ftp" - json_add_string "listen" ":$ftp_port" - json_add_int "find_pasv_port_attempts" "$find_pasv_port_attempts" - json_add_boolean "active_transfer_port_non_20" "$active_transfer_port_non_20" - json_add_int "idle_timeout" "$idle_timeout" - json_add_int "connection_timeout" "$connection_timeout" - json_add_boolean "disable_active_mode" "$disable_active_mode" - json_add_boolean "default_transfer_binary" "$default_transfer_binary" - json_add_boolean "enable_active_conn_ip_check" "$enable_active_conn_ip_check" - json_add_boolean "enable_pasv_conn_ip_check" "$enable_pasv_conn_ip_check" - json_close_object - - # sftp - json_add_object "sftp" - json_add_boolean "enable" "$sftp" - json_add_string "listen" ":$sftp_port" - json_close_object - json_dump > "$data_dir/config.json" procd_open_instance alist diff --git a/alist/files/data.db b/alist/files/data.db index 9079dcf4b..e1bb40dd3 100644 Binary files a/alist/files/data.db and b/alist/files/data.db differ diff --git a/alist/src/public/dist/assets/logo.png b/alist/src/public/dist/assets/logo.png deleted file mode 100644 index b1d76cb4f..000000000 Binary files a/alist/src/public/dist/assets/logo.png and /dev/null differ diff --git a/autoupdate/Makefile b/autoupdate/Makefile new file mode 100755 index 000000000..7eaa8c7b3 --- /dev/null +++ b/autoupdate/Makefile @@ -0,0 +1,32 @@ +# Copyright (C) 2020-2024 Hyy2001X + +include $(TOPDIR)/rules.mk + +PKG_NAME:=autoupdate +PKG_VERSION:=1 +PKG_RELEASE:=1 + +include $(INCLUDE_DIR)/package.mk + +define Package/$(PKG_NAME) + SECTION:=utils + CATEGORY:=Utilities + TITLE:=Upgrade OpenWrt by one-key + DEPENDS:=+bash +jq +endef + +define Package/$(PKG_NAME)/description + autoupdate - One-key AutoUpdate +endef + +define Build/Compile +endef + +define Package/$(PKG_NAME)/install + $(INSTALL_DIR) $(1)/bin + $(INSTALL_BIN) ./files/bin/autoupdate $(1)/bin + $(INSTALL_DIR) $(1)/etc/autoupdate + $(CP) ./files/etc/autoupdate/* $(1)/etc/autoupdate +endef + +$(eval $(call BuildPackage,$(PKG_NAME))) diff --git a/autoupdate/files/bin/autoupdate b/autoupdate/files/bin/autoupdate new file mode 100755 index 000000000..86c6fff2f --- /dev/null +++ b/autoupdate/files/bin/autoupdate @@ -0,0 +1,1445 @@ +#!/bin/bash +# AutoBuild Module by Hyy2001 +# AutoUpdate for Openwrt +# Dependences: wget-ssl/wget/uclient-fetch curl jq expr sysupgrade + +Version=V6.10.5 + +function TITLE() { + clear && echo "Openwrt-AutoUpdate Script by Hyy2001 ${Version}" +} + +function SHELL_HELP() { + local Next="${Grey}⌊ ...${White}" + TITLE + echo -e " +使用方法: bash $0 [-n] [-f] [-u] [-F] [-P] [-D ] [--path ] ... + bash $0 [-x] [--path ] [--url ] ... + +更新固件: + -n 不保留配置更新固件 * + -u 适用于定时更新 LUCI 的参数 * + -f 跳过版本号校验,并强制刷写固件 ${Red}(危险)${White} * + -F, --force-flash 强制刷写固件 ${Red}(危险)${White} * + -P, --proxy 优先开启镜像加速下载固件 * + ${Next} A 自动选择镜像地址 + ${Next} E ghproxy.cn + ${Next} F ghps.cc + ${Next} G ghgo.xyz + -D 使用指定的下载器 * + --decompress 解压 img.gz 固件后再更新固件 * + --skip-verify 跳过固件 SHA256 校验 ${Red}(危险)${White} * + --path 固件下载到用户提供的绝对路径 * + +更新程序: + -x 更新 autoupdate 程序 + -x -path 更新 autoupdate 程序 (保存程序到用户提供的绝对路径 ) * + -x -url 更新 autoupdate 程序 (使用提供的地址 更新程序) * + +其他参数: + --help 打印 AutoUpdate 程序帮助信息 + -C 更改 Github 地址为提供的 + --api 打印 Github API 内容 + --backup 备份当前系统配置到当前路径 + ${Next} -path 备份当前系统配置文件到用户提供的绝对路径 + --chk 检查 AutoUpdate 运行环境 + --clean 清理 AutoUpdate 缓存 + --flag 更改固件标签为提供的 + ${Next} -reset 恢复默认的固件标签 + --fw-log < | *> 打印 <当前 | 指定> 版本的固件更新日志 + --env 打印用户指定的环境变量 + ${Next} -list 打印环境变量列表 + --log 打印 AutoUpdate 运行日志 ${Green}(问题反馈)${White} + ${Next} -clean 清空程序运行日志 + ${Next} -path 更改 AutoUpdate 运行日志保存路径到用户提供的绝对路径 + --list 打印当前系统信息 + --reset 重置 AutoUpdate 运行环境 + --verbose 打印详细下载信息 * + -v < | [Cc]loud> 打印 <当前 | 云端> autoupdate 版本 + -V < | [Cc]loud> 打印 <当前 | 云端> 固件版本 + +程序、固件更新问题反馈请到 ${Github} 反馈,并上程序运行日志与系统信息 +" + exit +} + +function SHOW_VARIABLE() { + TITLE + cat < +EOF + echo + return 0 +} + +function MEMINFO() { + [[ ! $1 || ! $* =~ (All|Mem|Swap) ]] && return 1 + local Mem Swap All Result + Mem=$(free | grep Mem: | awk '{Mem=$7/1024} {printf("%.0f\n",Mem)}' 2> /dev/null) + Swap=$(free | grep Swap: | awk '{Swap=$4/1024} {printf("%.0f\n",Swap)}' 2> /dev/null) + All=$(expr ${Mem} + ${Swap} 2> /dev/null) + Result=$(eval echo '$'$1) + if [[ ${Result} ]] + then + LOGGER "[$1] 可用运行内存: [${Result}M]" + echo ${Result} + return 0 + else + LOGGER "[$1] 可用内存获取失败!" + return 1 + fi +} + +function SPACEINFO() { + [[ ! $1 ]] && return 1 + local Result Path + Path="$(awk -F '/' '{print $2}' <<< $1)" + Result="$(df -m /${Path} 2> /dev/null | grep -v Filesystem | awk '{print $4}')" + if [[ ${Result} ]] + then + LOGGER "[/${Path}] 可用存储空间: [${Result}M]" + echo "${Result}" + return 0 + else + LOGGER "[/${Path}] 可用存储空间获取失败!" + return 1 + fi +} + +function RM() { + for i in $@ + do + rm -r "$i" 2> /dev/null + LOGGER "删除文件: [$i]" + done +} + +GET_DOWNLOADER() { + for i in ${DL_DEPENDS[@]} + do + if [[ $(CHECK_PKG $i) == true ]] + then + echo "$i" + return 0 + fi + done + LOGGER "没有可用的下载器!" + return 1 +} + +function STRING() { + [[ $# -gt 3 ]] && return + case $1 in + -f) + shift + [[ ! -r $1 ]] && return + grep -E -q $2 $1 2> /dev/null && echo -n $2 + ;; + *) + echo -n $1 | grep -E -q $2 2> /dev/null && echo -n $2 + ;; + esac + return +} + +function LIST_ENV() { + local ENV + case $1 in + -list) + cat ${Config_Default} ${Config_Custom} 2> /dev/null | grep -v '#' | while read ENV;do + case $2 in + 1 | 2) + [[ ${ENV} ]] && eval echo ${ENV} | awk -F '=' '{print $"'$2'"}' + ;; + *) + [[ ${ENV} ]] && eval echo ${ENV} + ;; + esac + done + return 0 + ;; + *) + while [[ $1 ]];do + ENV=$1 + if [[ ! ${ENV} =~ (-) && ! ${ENV} == [0-9] && ${ENV} =~ [0-9a-zA-Z] ]] + then + if [[ $(eval echo \$\{#$ENV[@]\}) -gt 1 ]] + then + eval echo \$\{$ENV[@]\} + else + # ENV_Result="$(GET_VARIABLE $ENV ${Config_Custom})" + # [[ ! ${ENV_Result} ]] && ENV_Result="$(GET_VARIABLE $ENV ${Config_Default})" + [[ ! ${ENV_Result} ]] && ENV_Result="$(eval echo \$$(eval echo '$'{ENV}))" + echo "${ENV_Result}" + unset ENV_Result + fi + fi + shift + done + return 0 + ;; + esac +} + +function CHECK_ENV() { + while [[ $1 ]];do + if [[ $(GET_VARIABLE $1 ${Config_Default} 2> /dev/null) ]] + then + ECHO y "环境变量: [${1}=$(GET_VARIABLE $1 ${Config_Default})]" + + else + ECHO r "环境变量: [$1] ... 错误" + fi + shift + done +} + +function CHECK_PKG() { + local Result="$(command -v $1 2> /dev/null)" + if [[ ${Result} && $? == 0 ]] + then + echo true + return 0 + else + LOGGER "检查软件包: [$1] ... 错误" + echo false + return 1 + fi +} + +function ECHO() { + local Color + if [[ ! $1 ]] + then + echo -ne "\n${Grey}[$(date "+%H:%M:%S")]${White} " + else + while [[ $1 ]];do + case $1 in + r | g | b | y | x | w) + case $1 in + r) Color="${Red}";; + g) Color="${Green}";; + b) Color="${Blue}";; + y) Color="${Yellow}";; + x) Color="${Grey}";; + w) Color="${White}";; + esac + shift + ;; + *) + Message=$1 + break + ;; + esac + done + echo -e "\n${Grey}[$(date "+%H:%M:%S")]${White}${Color} ${Message}${White}" + LOGGER "${Message}" + fi +} + +function LOGGER() { + if [[ ! $* =~ (--help|--log) ]] + then + [[ ! -d ${Log_Path} ]] && mkdir -p ${Log_Path} + [[ ! -f ${Log_Path}/${Log_File} ]] && touch -f ${Log_Path}/${Log_File} + echo "[$(date "+%H:%M:%S")] [$$] $*" >> ${Log_Path}/${Log_File} + fi +} + +function RANDOM() { + head -n 5 /dev/urandom | md5sum | cut -c 1-$1 +} + +function GET_SHA256SUM() { + local Result="$(sha256sum $1 2> /dev/null | cut -c1-$2)" + if [[ ${Result} && $? == 0 ]] + then + echo "${Result}" + return 0 + else + return 1 + fi +} + +function GET_VARIABLE() { + local Result="$(grep "$1=" $2 2> /dev/null | grep -v "#" | awk -F '=' '{print $2}')" + if [[ ${Result} && $? == 0 ]] + then + eval echo "${Result}" + return 0 + else + return 1 + fi +} + +function EDIT_VARIABLE() { + local Mode=$1 + shift + [[ ! -s $1 ]] && ECHO r "未检测到环境变量文件: [$1] !" && return 1 + case "${Mode}" in + edit) + if [[ ! $(GET_VARIABLE $2 $1) ]] + then + LOGGER "[EDIT_VARIABLE] 新增环境变量 [$2 = $3]" + echo -e "\n$2=$3" >> $1 + RETURN=0 + else + sed -i "s?$(GET_VARIABLE $2 $1)?$3?g" $1 2> /dev/null + if [[ $? == 0 ]] + then + LOGGER "[EDIT_VARIABLE] 环境变量 [$2 > $3] 修改成功!" + RETURN=0 + else + LOGGER "[EDIT_VARIABLE] 环境变量 [$2 > $3] 修改失败!" + RETURN=1 + fi + fi + ;; + rm) + sed -i "/$2=/d" $1 + if [[ $? == 0 ]] + then + LOGGER "[EDIT_VARIABLE] 从 $1 删除环境变量 [$2] ... 成功" + RETURN=0 + else + LOGGER "[EDIT_VARIABLE] 从 $1 删除环境变量 [$2] ... 失败" + RETURN=1 + fi + ;; + esac + cp -a ${Config_Custom} ${Tmp_Path}/custom + grep -vE "^[[:blank:]]*$" ${Tmp_Path}/custom > ${Config_Custom} + echo >> ${Config_Custom} + return ${RETURN} +} + +function LOAD_CONFIG() { + while [[ $1 ]];do + if [[ -s $1 ]] + then + chmod +x $1 + source $1 + else + LOGGER "未检测到环境变量列表: [$1]" + fi + shift + done +} + +function LOAD_VARIABLE() { + CHECK_PKG_DEPENDS -f ${PKG_DEPENDS[@]} + for i in ${ENV_DEPENDS[@]};do + local ENV="$(GET_VARIABLE ${i} $1)" + local _ENV="$(GET_VARIABLE ${i} $2)" + if [[ ${_ENV} ]] + then + ENV=${_ENV} + fi + if [[ ! ${ENV} ]] + then + ECHO r "未检测到环境变量: [${i}]" + sleep 1 + fi + eval ${i}="${ENV}" 2> /dev/null + done + unset i ENV + + [[ ! ${TARGET_PROFILE} ]] && TARGET_PROFILE="$(jq .model.id /etc/board.json 2> /dev/null)" + [[ ! ${TARGET_PROFILE} || ${TARGET_PROFILE} == null ]] && ECHO r "当前设备名称获取失败!" && exit 1 + [[ ! ${OP_VERSION} ]] && OP_VERSION="未知" + DISTRIB_TARGET="$(GET_VARIABLE DISTRIB_TARGET /etc/openwrt_release)" + TARGET_BOARD="$(cut -d '/' -f1 <<< ${DISTRIB_TARGET})" + TARGET_SUBTARGET="$(cut -d '/' -f2 <<< ${DISTRIB_TARGET})" + if [[ ! ${Github} ]] + then + _Github=$(awk '{print $4}' /proc/version | cut -d '@' -f2 2> /dev/null | cut -d ')' -f1) + if [[ $(grep -q 'http' <<< ${_Github} ; echo $?) == 0 ]] + then + Github=${_Github} + fi + fi + Firmware_Author="${Github##*com/}" + Github_Release="${Github}/releases/download/AutoUpdate" + Github_Raw="https://raw.githubusercontent.com/${Firmware_Author}/master" + Github_API="https://api.github.com/repos/${Firmware_Author}/releases/latest" + Log_Full="${Log_Path}/${Log_File}" +} + +function ALTER_GITHUB() { + if [[ ! $1 =~ https://github.com/ || $# != 1 ]] + then + ECHO r "Github 地址格式错误,正确示例: [https://github.com/Hyy2001X/AutoBuild-Actions]" + return 1 + fi + UCI_Github="$(uci get autoupdate.@autoupdate[0].github 2> /dev/null)" + if [[ ${UCI_Github} && ! ${UCI_Github} == $1 ]] + then + uci set autoupdate.@autoupdate[0].github=$1 2> /dev/null + uci commit autoupdate + LOGGER "UCI Github 地址已修改为 [$1]" + fi + if [[ ! ${Github} == $1 ]] + then + EDIT_VARIABLE edit ${Config_Custom} Github $1 + if [[ $? == 0 ]] + then + ECHO y "Github 地址已修改为: [$1]" + else + ECHO y "Github 地址修改失败!" + return 1 + fi + REMOVE_CACHE + else + ECHO g "Github 地址未修改!" + fi + return 0 +} + +function ALTER_FLAG() { + case $1 in + -reset) + EDIT_VARIABLE rm ${Config_Custom} TARGET_FLAG + uci set autoupdate.@autoupdate[0].flag="$(GET_VARIABLE TARGET_FLAG /rom/${Config_Default})" 2> /dev/null + uci commit autoupdate + ECHO y "固件标签已恢复为: [$(GET_VARIABLE TARGET_FLAG ${Config_Default})]" + return 0 + ;; + *) + if [[ ! $1 =~ (\"|=|-|_|\.|\#|\|) && $1 =~ [a-zA-Z0-9] ]] + then + UCI_Flag="$(uci get autoupdate.@autoupdate[0].flag 2> /dev/null)" + if [[ ${UCI_Flag} && ! ${UCI_Flag} == $1 ]] + then + uci set autoupdate.@autoupdate[0].flag=$1 2> /dev/null + uci commit autoupdate + LOGGER "UCI 固件标签已修改为: [$1]" + fi + if [[ ! ${TARGET_FLAG} == $1 ]] + then + EDIT_VARIABLE edit ${Config_Custom} TARGET_FLAG $1 + ECHO r "警告: 修改此设置后可能导致无法检测到最新固件!" + ECHO r "后续执行指令: [$0 --flag -reset] 可进行标签恢复!" + ECHO y "固件标签已修改为: [$1]" + else + ECHO g "固件标签未修改!" + fi + return 0 + else + ECHO r "错误的参数: [$1],当前仅支持 [a-zA-Z0-9] 且不能包含 <\" = - _ # |> 等特殊符号!" + return 1 + fi + ;; + esac +} + +function UPDATE_SCRIPT() { + if [[ ! -d $1 ]] + then + mkdir -p $1 2> /dev/null || { + ECHO r "程序保存路径 [$1] 创建失败!" + return 1 + } + fi + ECHO "程序保存路径: [$1]" + DOWNLOADER --file-name autoupdate --no-url-name --dl ${DL_DEPENDS[@]} --url $2 --path ${Tmp_Path} --timeout 5 --type 更新文件 + if [[ $? == 0 && -f ${Tmp_Path}/autoupdate ]] + then + chmod +x ${Tmp_Path}/autoupdate + Script_Version="$(awk -F '=' '/Version/{print $2}' ${Tmp_Path}/autoupdate | awk 'NR==1')" + mv -f ${Tmp_Path}/autoupdate $1 + if [[ $? == 0 ]] + then + ECHO y "[${Version} > ${Script_Version}] autoupdate 程序更新成功!" + REMOVE_CACHE + return 0 + else + ECHO r "autoupdate 程序移动失败!" + return 1 + fi + else + ECHO r "autoupdate 程序更新失败!" + return 1 + fi +} + +function CHECK_PKG_DEPENDS() { + case $1 in + -e) + shift + TITLE + printf "\n%-28s %-5s\n" 软件包 检测结果 + while [[ $1 ]];do + printf "%-25s %-5s\n" $1 $(CHECK_PKG $1) + shift + done + ECHO y "软件包检测结束,请尝试手动安装测结果为 [false] 的软件包!" + ;; + -f) + shift + while [[ $1 ]];do + CHECK_PKG $1 > /dev/null 2>&1 + if [[ $? != 0 ]] + then + ECHO r "未安装软件包: [$1]" + exit 1 + fi + shift + done + ;; + esac +} + +function CHECK_TIME() { + [[ -s $1 && -n $(find $1 -type f -mmin -$2) ]] && { + echo true + return 0 + } || { + RM $1 + echo false + return 1 + } +} + +function ANALYZE_API() { + [[ ! ${Github_Release} ]] && { + ECHO r "Github API 地址为空!" + exit 1 + } + local API_Cache=${Tmp_Path}/API_Cache + if [[ $(CHECK_TIME ${API_File} 1) == false ]] + then + DOWNLOADER --path ${Tmp_Path} --file-name API_Cache --dl ${DL_DEPENDS[@]} --url "${Github_API}@@1 $(Proxy_X ${Github_Release}/API G@@1 F@@1 E@@1)" --no-url-name --timeout 5 + [[ ! $? == 0 || -z $(cat ${API_Cache} 2> /dev/null) ]] && { + ECHO r "Github API 请求错误,请检查网络后再试!" + exit 2 + } + fi + [[ -f ${API_File} ]] && RM ${API_File} + touch -a ${API_File} + LOGGER "开始解析 Github API ..." + for i in $(seq 0 $(jq ".assets | length" ${API_Cache} 2> /dev/null));do + eval name=$(jq ".assets[${i}].name" ${API_Cache} 2> /dev/null) + [[ ${name} == null ]] && continue + case ${name} in + AutoBuild-${OP_REPO}-${TARGET_PROFILE}-* | Update_Logs.json) + LOGGER "可用固件/日志: ${name}" + eval format=$(grep -E -o '\.[a-z]+.+' <<< ${name} | cut -c2-10) + eval version=$(grep -E -o "R[0-9.]+-[0-9]+" <<< ${name}) + eval sha256=$(grep -E -o "\-[a-z0-9]+" <<< ${name} | cut -c2-6 | awk 'END{print}') + eval browser_download_url=$(jq ".assets[${i}].browser_download_url" ${API_Cache} 2> /dev/null) + eval size=$(jq ".assets[${i}].size" ${API_Cache} 2> /dev/null | awk '{a=$1/1048576} {printf("%.2f\n",a)}') + eval updated_at=$(jq ".assets[${i}].updated_at" ${API_Cache} 2> /dev/null | sed 's/[-:TZ]//g') + eval download_count=$(jq ".assets[${i}].download_count" ${API_Cache} 2> /dev/null) + [[ ! ${version} || ${version} == null ]] && version="-" + [[ ! ${browser_download_url} || ${browser_download_url} == null ]] && continue + [[ ! ${size} || ${size} == null || ${size} == 0 ]] && size="-" || size="${size}MB" + [[ ! ${updated_at} || ${updated_at} == null ]] && updated_at="-" + [[ ! ${download_count} || ${download_count} == null ]] && download_count="-" + [[ ! ${sha256} || ${sha256} == null ]] && sha256="-" + printf "%-75s %-15s %-5s %-8s %-20s %-10s %-15s %s\n" ${name} ${format} ${download_count} ${sha256} ${version} ${updated_at} ${size} ${browser_download_url} | grep -E -v "${REGEX_Skip_Format}" >> ${API_File} + ;; + esac + done + unset i + if [[ ! $(cat ${API_File} 2> /dev/null) ]] + then + ECHO r "Github API 解析内容为空!" + exit 1 + else + LOGGER "Github API 解析成功!" + return 0 + fi +} + +function GET_FW_INFO() { + local Info Type Result + [[ ! -s ${API_File} ]] && { + ECHO r "未检测到 API 文件!" + exit 1 + } + Info=$(grep "AutoBuild-${OP_REPO}-${TARGET_PROFILE}" ${API_File} | grep "${TARGET_FLAG}" | awk 'BEGIN {MAX = 0} {if ($6+0 > MAX+0) {MAX=$6 ;content=$0} } END {print content}') + Result="$(awk '{print $"'${1}'"}' <<< ${Info} 2> /dev/null)" + case $1 in + 1) Type="固件名称";; + 2) Type="固件格式";; + 3) Type="下载次数";; + 4) Type="校验信息";; + 5) Type="固件版本";; + 6) Type="发布日期";; + 7) Type="固件体积";; + 8) Type="固件链接";; + *) Type="未定义信息";; + esac + [[ ! ${Result} == "-" ]] && { + LOGGER "${Type}: ${Result}" + echo -e "${Result}" + } || { + LOGGER "${Type}获取失败!" + return 1 + } +} + +function GET_CLOUD_LOG() { + local Version log_Test + [[ ! $(cat ${API_File} 2> /dev/null) =~ Update_Logs.json ]] && { + LOGGER "未检测到已部署的云端日志,跳过下载 ..." + return 1 + } + case $1 in + [Ll]ocal) + Version="${OP_VERSION}" + ;; + [Cc]loud) + Version="$(GET_FW_INFO 5)" + ;; + *) + Version=$1 + ;; + esac + [[ $(CHECK_TIME ${Tmp_Path}/Update_Logs.json 1) == false ]] && { + DOWNLOADER --path ${Tmp_Path} --file-name Update_Logs.json --dl ${DL_DEPENDS[@]} --url "$(Proxy_X ${Github_Release} G@@1 F@@1 E@@1)" --timeout 5 + } + [[ ! -s ${Tmp_Path}/Update_Logs.json ]] && return 1 + log_Test="$(jq '."'"${TARGET_PROFILE}"'"."'"${Version}"'"' ${Tmp_Path}/Update_Logs.json 2> /dev/null)" + if [[ ${log_Test} && ${log_Test} != null ]] + then + echo -e "\n${Grey}${Version} 固件更新日志:${Green}\n" + jq '."'"${TARGET_PROFILE}"'"."'"${Version}"'"' ${Tmp_Path}/Update_Logs.json 2> /dev/null | grep -E -v "\[|\]" + echo -e "${White}" + else + LOGGER "未获取到 [${Version}] 固件的日志信息!" + fi +} + +function UPGRADE() { + TITLE + LOGGER "结束其他 autoupdate 进程 ..." + KILL_PROCESS ${Script_File} + LOGGER "运行: $0 $*" + Firmware_Path="${Tmp_Path}" + Upgrade_Option="$(command -v sysupgrade) -q" + MSG="更新固件" + while [[ $1 ]];do + case $1 in + -T) + Special_Commands="${Special_Commands} [测试模式]" + ;; + -P | --proxy) + case $2 in + A) + Proxy_Type="All" + Special_Commands="${Special_Commands} [镜像加速 Automatic]" + shift + ;; + E) + Proxy_Type=$2 + Special_Commands="${Special_Commands} [ghproxy.cn]" + shift + ;; + F) + Proxy_Type=$2 + Special_Commands="${Special_Commands} [ghps.cc]" + shift + ;; + G) + Proxy_Type=$2 + Special_Commands="${Special_Commands} [ghgo.xyz]" + shift + ;; + *) + Proxy_Type="All" + Special_Commands="${Special_Commands} [镜像加速 Automatic]" + shift + ;; + esac + ;; + -D) + DL_DEPENDS=($2) + Special_Commands="${Special_Commands} [$1 ${DL_DEPENDS[@]}]" + shift + ;; + -F | --force-flash) + Force_Flash=1 + Special_Commands="${Special_Commands} [强制刷写]" + ;; + --decompress) + Special_Commands="${Special_Commands} [优先解压固件]" + Decompress_Mode=1 + ;; + -f) + Force_Mode=1 + Force_Flash=1 + Special_Commands="${Special_Commands} [强制模式]" + ;; + -n) + Upgrade_Option="${Upgrade_Option} -n" + Special_MSG=" (不保留配置)" + ;; + --path) + Firmware_Path=$2 + ECHO g "固件保存路径: [${Firmware_Path} | $(SPACEINFO ${Firmware_Path})M]" + shift + ;; + --skip-verify) + Skip_Verify_Mode=1 + Special_Commands="${Special_Commands} [跳过 SHA256 验证]" + ;; + -u) + Nobody_Mode=1 + Special_Commands="${Special_Commands} [定时更新]" + ;; + --verbose) + Special_Commands="${Special_Commands} [详细信息]" + ;; + *) + LOGGER "跳过未知参数: [$1] ..." + shift + esac + shift + done + [[ ${Force_Flash} == 1 ]] && Upgrade_Option="${Upgrade_Option} -F" + [[ ${Special_Commands} ]] && ECHO g "特殊指令:${Special_Commands} | ${Upgrade_Option}" + ECHO g "执行: ${MSG}${Special_MSG}" + if [[ ! ${Proxy_Type} ]] + then + [[ $(NETWORK_CHECK https://www.google.com/generate_204) != true ]] && { + ECHO r "Google 连接错误,优先使用镜像加速下载!" + Proxy_Type="All" + } + fi + ECHO "正在检查固件版本更新 ..." + ANALYZE_API + CLOUD_FW_Name=$(GET_FW_INFO 1) + CLOUD_FW_Format=$(GET_FW_INFO 2) + CLOUD_FW_Count=$(GET_FW_INFO 3) + CLOUD_FW_SHA256=$(GET_FW_INFO 4) + CLOUD_FW_Version=$(GET_FW_INFO 5) + CLOUD_FW_Date=$(GET_FW_INFO 6) + CLOUD_FW_Size=$(GET_FW_INFO 7) + CLOUD_FW_Url=$(GET_FW_INFO 8) + [[ ! ${CLOUD_FW_Name} || -z ${CLOUD_FW_Url} ]] && { + ECHO r "检查更新失败,请检查网络后再试!" + exit 2 + } + [[ ${CLOUD_FW_Version} == ${OP_VERSION} ]] && { + CURRENT_Type="${Yellow} [已是最新]${White}" + Stop_Code=1 + } || { + [[ $(cut -d - -f2 <<< ${CLOUD_FW_Version}) -gt $(cut -d - -f2 <<< ${OP_VERSION}) ]] && CHECKED_Type="${Green} [可更新]${White}" + [[ $(cut -d - -f2 <<< ${CLOUD_FW_Version}) -lt $(cut -d - -f2 <<< ${OP_VERSION}) ]] && { + CHECKED_Type="${Red} [旧版本]${White}" + Stop_Code=2 + } + } + echo -e " +${Grey}### 系统 & 云端固件详情 ###${White} + +设备名称: ${TARGET_PROFILE} +固件标签: ${TARGET_FLAG} +内核版本: $(uname -sr) +$(echo "固件格式: ${CLOUD_FW_Format}") + +$(echo -e "当前固件版本: ${OP_VERSION}${CURRENT_Type}") +$(echo -e "云端固件版本: ${CLOUD_FW_Version}${CHECKED_Type}") + +云端固件名称: ${CLOUD_FW_Name} +云端固件体积: ${CLOUD_FW_Size} +固件下载次数: ${CLOUD_FW_Count}" + if [[ ${Force_Mode} != 1 ]] + then + (sync && echo 3 > /proc/sys/vm/drop_caches) 2> /dev/null + if [[ $(MEMINFO All) -lt $(echo ${CLOUD_FW_Size} | awk -F '.' '{print $1}') ]] + then + ECHO r "内存空间不足 [${CLOUD_FW_Size}],请尝试设置 Swap 交换分区或重启设备后再试!" + exit + fi + if [[ $(SPACEINFO ${Firmware_Path}) -lt $(awk -F '.' '{print $1}' <<< ${CLOUD_FW_Size}) ]] + then + ECHO r "设备空间不足 [${CLOUD_FW_Size}],请尝试更换固件保存路径后再试!" + exit + fi + fi + GET_CLOUD_LOG ${CLOUD_FW_Version} + case "${Stop_Code}" in + 1 | 2) + [[ ${Nobody_Mode} == 1 ]] && ECHO y "当前固件 [${OP_VERSION}] 已是最新版本,无需更新!" && exit 0 + [[ ${Stop_Code} == 1 ]] && err_MSG="当前固件 [${OP_VERSION}] 已是最新版本" || err_MSG="云端固件版本为旧版" + [[ ! ${Force_Mode} == 1 ]] && { + ECHO && read -p "${err_MSG},是否继续更新固件?[Y/n]:" Choose + } || Choose=Y + [[ ! ${Choose} =~ [Yy] ]] && { + ECHO x "已取消固件更新操作,退出更新程序 ..." + exit 0 + } + ;; + esac + local URL + case "${Proxy_Type}" in + E | F | G) + URL="$(Proxy_X ${CLOUD_FW_Url} ${Proxy_Type}@@5)" + ;; + All) + URL="$(Proxy_X ${CLOUD_FW_Url} G@@2 X@@1 E@@1 F@@1)" + ;; + *) + URL="$(Proxy_X ${CLOUD_FW_Url} X@@2 G@@1 E@@1 F@@1)" + ;; + esac + DOWNLOADER --file-name ${CLOUD_FW_Name} --no-url-name --dl ${DL_DEPENDS[@]} --url ${URL} --path ${Firmware_Path} --timeout 15 --type 固件 + [[ ! -s ${Firmware_Path}/${CLOUD_FW_Name} || $? != 0 ]] && { + ECHO r "固件下载失败,请检查网络后再试!" + exit 1 + } + if [[ ! ${Skip_Verify_Mode} == 1 ]];then + if [[ $(GET_SHA256SUM ${Firmware_Path}/${CLOUD_FW_Name} 5) != ${CLOUD_FW_SHA256} ]] + then + ECHO r "SHA256 校验失败!" + exit 2 + fi + ECHO y "固件完整性校验通过,即将开始更新固件 ..." + fi + case "${CLOUD_FW_Format}" in + img.gz) + if [[ ${Decompress_Mode} == 1 ]] + then + ECHO "正在解压 gizp 格式固件 ..." + gzip -d -q -f -c ${Firmware_Path}/${CLOUD_FW_Name} > ${Firmware_Path}/$(sed -r 's/(.*).gz/\1/' <<< ${CLOUD_FW_Name}) + if [[ ! $? == 0 ]] + then + ECHO r "固件解压失败!" + exit 2 + else + CLOUD_FW_Name="$(sed -r 's/(.*).gz/\1/' <<< ${CLOUD_FW_Name})" + LOGGER "固件已解压到: [${Firmware_Path}/${CLOUD_FW_Name}]" + fi + else + if [[ $(CHECK_PKG gzip) == true ]] + then + LOGGER "卸载软件包 [gzip] ..." + opkg remove gzip > /dev/null 2>&1 + fi + fi + ;; + esac + if [[ ${Test_Mode} == 1 ]] + then + ECHO x "[测试模式] ${Upgrade_Option} ${Firmware_Path}/${CLOUD_FW_Name}" + exit 0 + else + DO_UPGRADE ${Upgrade_Option} ${Firmware_Path}/${CLOUD_FW_Name} + fi +} + +function DO_UPGRADE() { + ECHO r "警告: 固件更新期间请不要断开电源或进行其他操作!" + sleep 3 + ECHO g "正在更新固件,请耐心等待 ..." + $* + if [[ $? != 0 ]] + then + sleep 5 + ECHO r "固件更新失败,请尝试使用 [autoupdate -F] 指令更新固件,执行此命令前请备份固件配置!" + exit 1 + else + ECHO y "固件更新成功,即将重启设备 ..." + sleep 3 + reboot + fi + exit +} + +function DOWNLOADER() { + local u E DL_Downloader DL_Name DL_URL DL_Path DL_Retries DL_Timeout DL_Type DL_Final No_URL_Name Print_Mode DL_Retires_All DL_URL_Final + while [[ $1 ]] + do + case $1 in + --dl) + shift + while [[ $1 ]] + do + case $1 in + wget* | curl | uclient-fetch) + [[ $(CHECK_PKG $1) == true ]] && { + DL_Downloader=$1 + break + } + shift + ;; + *) + LOGGER "[DOWNLOADER] 跳过未知下载器: [$1] ..." + shift + ;; + esac + done + while [[ $1 ]] + do + [[ $1 =~ '--' ]] && break + [[ ! $1 =~ '--' ]] && shift + done + if [[ ! ${DL_Downloader} ]] + then + ECHO r "没有可用的下载器!" + return 1 + fi + ;; + --file-name) + shift + DL_Name=$1 + while [[ $1 ]] + do + [[ $1 =~ '--' ]] && break + [[ ! $1 =~ '--' ]] && shift + done + ;; + --url) + shift + DL_URL=($(grep -E -o "https://.*@@[0-9]+|http://.*@@[0-9]+" <<< $@)) + if [[ ! ${DL_URL[*]} ]] + then + DL_URL=($1) + DL_URL_Count="${#DL_URL[@]}" + DL_Retires_All="${DL_URL_Count}" + else + DL_Retires_All="$(grep -E -o "@@[0-9]+" <<< ${DL_URL[*]} | grep -E -o "[0-9]+" | awk '{Sum += $1};END {print Sum}')" + DL_URL_Count="${#DL_URL[@]}" + fi + while [[ $1 ]] + do + [[ $1 =~ '--' ]] && break + [[ ! $1 =~ '--' ]] && shift + done + ;; + --no-url-name) + shift + No_URL_Name=1 + ;; + --path) + shift + DL_Path=$1 + if [[ ! -d ${DL_Path} ]] + then + mkdir -p ${DL_Path} 2> /dev/null || { + ECHO r "目标下载路径 [${DL_Path}] 创建失败!" + return 1 + } + fi + while [[ $1 ]] + do + [[ $1 =~ '--' ]] && break + [[ ! $1 =~ '--' ]] && shift + done + ;; + --timeout) + shift + if [[ ! $1 =~ [1-9] ]] + then + shift + else + DL_Timeout=$1 + while [[ $1 ]] + do + [[ $1 =~ '--' ]] && break + [[ ! $1 =~ '--' ]] && shift + done + fi + ;; + --type) + shift + DL_Type=$1 + while [[ $1 ]] + do + [[ $1 =~ '--' ]] && break + [[ ! $1 =~ '--' ]] && shift + done + ;; + --print) + shift + Print_Mode=1 + ;; + *) + shift + ;; + esac + done + case "${DL_Downloader}" in + wget*) + DL_Template="${DL_Downloader} --quiet --no-check-certificate --tries 1 --timeout 10 -O" + ;; + curl) + DL_Template="${DL_Downloader} --silent --insecure -L -k --connect-timeout 10 --retry 1 -o" + ;; + uclient-fetch) + DL_Template="${DL_Downloader} --quiet --no-check-certificate -4 --timeout 10 -O" + ;; + esac + if [[ ${Test_Mode} == 1 || ${Verbose_Mode} == 1 ]] + then + DL_Template="${DL_Template/ --quiet / }" + DL_Template="${DL_Template/ --silent / }" + fi + [[ ${DL_Timeout} ]] && DL_Template="${DL_Template/-timeout 10/-timeout ${DL_Timeout}}" + local E=0 ; while [[ ${E} != ${DL_URL_Count} ]] + do + DL_URL_Cache="${DL_URL[$E]}" + DL_Retries="${DL_URL_Cache##*@@}" + [[ ! ${DL_Retries} || ! ${DL_Retries} == [0-9] ]] && DL_Retries=1 + DL_URL_Final="${DL_URL_Cache%*@@*}" + for u in $(seq ${DL_Retries}) + do + sleep 1 + if [[ ! ${Failed} ]] + then + [[ ${DL_Type} ]] && ECHO "正在下载${DL_Type},请耐心等待 ..." + else + [[ ${DL_Type} ]] && ECHO "尝试重新下载,剩余重试次数: [${DL_Retires_All}]" + fi + if [[ ! ${DL_Name} ]] + then + DL_Name="${DL_URL_Final##*/}" + DL_Final="${DL_Template} ${DL_Path}/${DL_Name} ${DL_URL_Final}" + else + [[ ${No_URL_Name} == 1 ]] && DL_Final="${DL_Template} ${DL_Path}/${DL_Name} ${DL_URL_Final}" || \ + DL_Final="${DL_Template} ${DL_Path}/${DL_Name} ${DL_URL_Final}/${DL_Name}" + fi + [[ -s ${DL_Path}/${DL_Name} ]] && \ + RM ${DL_Path}/${DL_Name} + LOGGER "执行下载指令: [${DL_Final}]" + ${DL_Final} + if [[ $? == 0 && -s ${DL_Path}/${DL_Name} ]] + then + touch -a ${DL_Path}/${DL_Name} + if [[ ${Print_Mode} == 1 ]] + then + cat ${DL_Path}/${DL_Name} 2> /dev/null + RM ${DL_Path}/${DL_Name} + return 0 + else + [[ ${DL_Type} ]] && ECHO y "${DL_Type}下载成功!" + fi + return 0 + else + [[ ! ${Failed} ]] && local Failed=1 + DL_Retires_All=$((${DL_Retires_All} - 1)) + if [[ ${u} == ${DL_Retries} ]] + then + break 1 + else + [[ ${DL_Type} ]] && ECHO r "${DL_Type}下载失败!" + u=$((${u} + 1)) + fi + fi + done ; unset u + E=$((${E} + 1)) + done + RM ${DL_Path}/${DL_Name} + [[ ${DL_Type} ]] && ECHO r "${DL_Type}下载失败!" + return 1 +} + +function REMOVE_CACHE() { + rm -r ${Tmp_Path}/API \ + ${Tmp_Path}/Update_Logs.json \ + ${Tmp_Path}/API_Cache 2> /dev/null +} + +function LOG() { + case $1 in + -path) + [[ ! $2 ]] && SHELL_HELP + if [[ $2 == ${Log_Path} ]] + then + ECHO y "AutoUpdate 日志保存路径相同,无需修改!" + return 0 + fi + if [[ ! -d $2 ]] + then + mkdir -p $2 2> /dev/null || { + ECHO r "AutoUpdate 日志保存路径错误!" + return 1 + } + fi + EDIT_VARIABLE rm ${Config_Custom} Log_Path + EDIT_VARIABLE edit ${Config_Custom} Log_Path $2 + Log_Path=${Log_Path} + uci set autoupdate.@autoupdate[0].logpath=$2 2> /dev/null + uci commit autoupdate + ECHO y "AutoUpdate 日志保存路径已修改为: [$2]!" + return 0 + ;; + -del | -rm | -clean) + RM ${Log_Path}/${Log_File} + return 0 + ;; + *) + if [[ -s ${Log_Path}/${Log_File} ]] + then + TITLE && echo + cat ${Log_Path}/${Log_File} 2> /dev/null + return 0 + else + return 1 + fi + ;; + esac +} + +function Proxy_X() { + local URL=$1 Type URL_Final + + [[ ${URL} =~ raw.githubusercontent.com ]] && Type=raw + [[ ${URL} =~ releases/download ]] && Type=release + [[ ${URL} =~ codeload.github.com ]] && Type=codeload + + case "${Type}" in + raw) + E=https://ghproxy.cn/${URL} + F=https://ghps.cc/${URL} + G=https://ghgo.xyz/${URL} + ;; + release) + E=https://ghproxy.cn/${URL} + F=https://ghps.cc/${URL} + G=https://ghgo.xyz/${URL} + ;; + codeload) + E=https://ghproxy.cn/${URL} + F=https://ghps.cc/${URL} + G=https://ghgo.xyz/${URL} + ;; + esac + while [[ $1 ]];do + local URL_Cache=$1 URL_Final + case $1 in + E@@*) + URL_Final="${URL_Cache/E/${E}}" + ;; + F@@*) + URL_Final="${URL_Cache/F/${F}}" + ;; + G@@*) + URL_Final="${URL_Cache/G/${G}}" + ;; + X@@*) + URL_Final="${URL_Cache/X/${URL}}" + ;; + esac + [[ ${URL_Final} ]] && { + echo "${URL_Final}" + } + unset URL_Final + shift + done +} + +function NETWORK_CHECK() { + case $(GET_DOWNLOADER) in + curl) + local Result=$(curl -I -o /dev/null -skL --connect-timeout 3 --retry 0 -w %{http_code} $1 2> /dev/null | tail -n1) + if [[ ${Result} == 204 ]] + then + echo true + LOGGER "[$(GET_DOWNLOADER)] [$1] 连通性测试正常!" + return 0 + fi + ;; + wget | wget-ssl) + rm -f "${Tmp_Path}/generate_204" > /dev/null 2>&1 + $(GET_DOWNLOADER) "$1" -q --no-check-certificate --spider --timeout=3 --no-dns-cache -4 --tries 1 + if [[ $? == 0 ]] + then + echo true + LOGGER "[$(GET_DOWNLOADER)] [$1] 连通性测试正常!" + return 0 + fi + ;; + uclient-fetch) + rm -f "${Tmp_Path}/generate_204" > /dev/null 2>&1 + uclient-fetch "$1" --quiet --no-check-certificate --spider --timeout=3 + if [[ $? == 0 ]] + then + echo true + LOGGER "[$(GET_DOWNLOADER)] [$1] 连通性测试正常!" + return 0 + fi + ;; + *) + return 1 + ;; + esac + echo false + LOGGER "[$(GET_DOWNLOADER)] [$1] 连通性测试失败!" + return 1 +} + +function AutoUpdate_Main() { + LOAD_CONFIG ${Config_Custom} + if [[ ! $1 =~ (-H|--help|--chk|--log) ]] + then + LOAD_VARIABLE ${Config_Default} ${Config_Custom} + fi + [[ ! -d ${Tmp_Path} ]] && mkdir -p ${Tmp_Path} + [[ ! $* ]] && UPGRADE $* + + local Input=($@) E=0 F Custom_Path Custom_URL + while :;do + F="${Input[${E}]}" + case "${F}" in + -T) + Test_Mode=1 + ;; + --verbose) + Verbose_Mode=1 + ;; + -path) + Custom_Path="${Input[$((${E} + 1))]}" + [[ ! ${Custom_Path} ]] && { + ECHO r "请输入正确的路径!" + } + ;; + -url) + Custom_URL="${Input[$((${E} + 1))]}" + [[ ! ${Custom_URL} || ! ${Custom_URL} =~ (https://*|http://*|ftp://*) ]] && { + ECHO r "链接格式错误,请输入正确的链接!" + exit 1 + } + ;; + -D) + case "${Input[$((${E} + 1))]}" in + wget* | curl | uclient-fetch) + DL_DEPENDS=(${Input[$((${E} + 1))]}) + ;; + *) + ECHO r "暂不支持当前下载器: [${Input[$((${E} + 1))]}]" + exit 1 + ;; + esac + ;; + esac + [[ ${E} == ${#Input[@]} ]] && break + E=$((${E} + 1)) + done + + while [[ $1 ]];do + case $1 in + -n | -f | -u | -T | -P | --proxy | -F | --force-flash | --verbose | --decompress | --skip-verify | -D | --path) + UPGRADE $* + exit $? + ;; + --api) + REMOVE_CACHE + ANALYZE_API > /dev/null 2>&1 + [[ $? == 0 ]] && cat ${API_File} 2> /dev/null + exit $? + ;; + --backup) + local Backup_File="backup-$(uname -n)-$(date +%Y-%m-%d)-$(RANDOM 5).tar.gz" + shift + [[ $# -gt 1 ]] && SHELL_HELP + if [[ ! $1 ]] + then + Backup_File="$(pwd)/${Backup_File}" + else + if [[ ! -d $1 ]];then + mkdir -p $1 || { + ECHO r "备份存放路径 [$1] 创建失败!" + exit 1 + } + fi + Backup_File="$1/${Backup_File}" + fi + ECHO "正在备份系统文件到 [${Backup_File}] ..." + $(command -v sysupgrade) -b "${Backup_File}" > /dev/null 2>&1 + if [[ $? == 0 ]] + then + ECHO y "备份文件创建成功!" + exit 0 + else + ECHO r "备份文件 [${Backup_File}] 创建失败!" + exit 1 + fi + ;; + --clean) + REMOVE_CACHE + exit + ;; + --chk) + shift + CHECK_PKG_DEPENDS -e ${PKG_DEPENDS[@]} ${DL_DEPENDS[@]} + if [[ $(NETWORK_CHECK https://connectivitycheck.platform.hicloud.com/generate_204) == false ]] + then + ECHO r "HiCloud 连接错误!" + else + ECHO y "HiCloud 连接正常!" + fi + if [[ $(NETWORK_CHECK https://www.google.com/generate_204) == false ]] + then + ECHO r "Google 连接错误!" + else + ECHO y "Google 连接正常!" + fi + CHECK_ENV ${ENV_DEPENDS[@]} + exit $? + ;; + --env) + shift + LIST_ENV $* + exit $? + ;; + --flag) + shift + [[ -z $* || $# != 1 ]] && SHELL_HELP + ALTER_FLAG $1 + exit + ;; + -V) + shift + case $1 in + [Cc]loud) + shift + ANALYZE_API > /dev/null 2>&1 + GET_FW_INFO $* 5 + ;; + *) + echo "${OP_VERSION}" + ;; + esac + exit + ;; + --fw-log) + shift + ANALYZE_API + if [[ ! $* ]] + then + GET_CLOUD_LOG local + else + GET_CLOUD_LOG $* + fi + exit + ;; + --list) + shift + SHOW_VARIABLE + exit $? + ;; + -v) + shift + case $1 in + [Cc]loud) + URL="$(Proxy_X ${Script_Url} G@@1 X@@1)" + DOWNLOADER --dl ${DL_DEPENDS[@]} --url ${URL} --path ${Tmp_Path} --print --type 临时程序 | grep -E -o "V[0-9].+" + ;; + *) + echo ${Version} + ;; + esac + exit + ;; + -x) + shift + URL="$(Proxy_X ${Script_Url} X@@1 G@@1 F@@1)" + [[ ${Custom_Path} ]] && Script_Path="${Custom_Path}" + [[ ${Custom_URL} ]] && URL="${Custom_URL}" + UPDATE_SCRIPT ${Script_Path} ${URL} + exit $? + ;; + -C) + shift + ALTER_GITHUB $* + exit $? + ;; + --help) + SHELL_HELP + exit 0 + ;; + --log) + shift + LOG $* + exit $? + ;; + --reset) + rm -r ${Config_Default} ${Config_Custom} + cp -a /rom/$(dirname ${Config_Default})/* $(dirname ${Config_Default})/ + cp -a /rom/etc/config/autoupdate /etc/config + REMOVE_CACHE + exit 0 + ;; + *) + SHELL_HELP + exit 1 + ;; + esac + done +} + +function KILL_PROCESS() { + local i ; for i in $(ps -ef 2> /dev/null | grep -v grep | grep $1 | grep -v $$ | awk '{print $1}' 2> /dev/null) + do + kill -9 ${i} 2> /dev/null & + LOGGER "结束进程: ${i}" + done +} + +Script_Path="$(cd $(dirname $0) 2> /dev/null ; pwd 2> /dev/null)" +Script_File="$0" +Script_Url="https://raw.githubusercontent.com/Hyy2001X/AutoBuild-Packages/master/autoupdate/files/bin/autoupdate" + +Tmp_Path=/tmp/autoupdate +Config_Path=/etc/autoupdate + +Log_Path=/tmp +Log_File=autoupdate.log + +API_File="${Tmp_Path}/API" + +Config_Default="${Config_Path}/default" +Config_Custom="${Config_Path}/custom" + +ENV_DEPENDS=( + Author + Github + TARGET_PROFILE + TARGET_FLAG + OP_VERSION + OP_AUTHOR + OP_BRANCH + OP_REPO +) +PKG_DEPENDS=( + jq + expr + sysupgrade +) +DL_DEPENDS=( + wget-ssl + curl + wget + uclient-fetch +) +REGEX_Skip_Format=".vdi|.vhdx|.vmdk|kernel|rootfs|factory" + +White="\e[0m" +Yellow="\e[33m" +Red="\e[31m" +Blue="\e[34m" +Grey="\e[36m" +Green="\e[32m" + +[[ $* ]] && COMMAND="$0 $*" || COMMAND="$0" +AutoUpdate_Main $* + diff --git a/autoupdate/files/etc/autoupdate/default b/autoupdate/files/etc/autoupdate/default new file mode 100755 index 000000000..6c74bffd8 --- /dev/null +++ b/autoupdate/files/etc/autoupdate/default @@ -0,0 +1,2 @@ +## 请不要修改此文件中的内容, 自定义变量请在 custom 中添加或修改 +## 该文件将在运行 autoupdate 脚本时被读取, 且该文件中的内容优先级低于 custom diff --git a/brook/Makefile b/brook/Makefile new file mode 100644 index 000000000..aeb5b93a3 --- /dev/null +++ b/brook/Makefile @@ -0,0 +1,45 @@ +# SPDX-License-Identifier: GPL-3.0-only +# +# Copyright (C) 2021-2023 ImmortalWrt.org + +include $(TOPDIR)/rules.mk + +PKG_NAME:=brook +PKG_VERSION:=20250808 +PKG_RELEASE:=1 + +PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz +PKG_SOURCE_URL:=https://codeload.github.com/txthinking/brook/tar.gz/v$(PKG_VERSION)? +PKG_HASH:=d78e8066ba5377c3841c8b6dcc6949cccbc04f3e475a3ac34587721438cde494 + +PKG_MAINTAINER:=Tianling Shen +PKG_LICENSE:=GPL-3.0 +PKG_LICENSE_FILES:=LICENSE + +PKG_BUILD_DEPENDS:=golang/host +PKG_BUILD_PARALLEL:=1 +PKG_USE_MIPS16:=0 +PKG_BUILD_FLAGS:=no-mips16 + +GO_PKG:=github.com/txthinking/brook +GO_PKG_BUILD_PKG:=$(GO_PKG)/cli/brook + +include $(INCLUDE_DIR)/package.mk +include $(TOPDIR)/feeds/packages/lang/golang/golang-package.mk + +define Package/brook + SECTION:=net + CATEGORY:=Network + SUBMENU:=Web Servers/Proxies + TITLE:=A cross-platform proxy software + DEPENDS:=$(GO_ARCH_DEPENDS) +ca-bundle + URL:=https://github.com/txthinking/brook +endef + +define Package/brook/description + Brook is a cross-platform strong encryption and not detectable proxy. + Zero-Configuration. +endef + +$(eval $(call GoBinPackage,brook)) +$(eval $(call BuildPackage,brook)) diff --git a/chinadns-ng/Makefile b/chinadns-ng/Makefile new file mode 100644 index 000000000..5c2162ef8 --- /dev/null +++ b/chinadns-ng/Makefile @@ -0,0 +1,72 @@ +# SPDX-License-Identifier: GPL-2.0-only + +include $(TOPDIR)/rules.mk + +PKG_NAME:=chinadns-ng +PKG_VERSION:=2025.08.09 +PKG_RELEASE:=1 + +ifeq ($(ARCH),aarch64) + ifeq ($(BOARD),rockchip) + PKG_ARCH:=chinadns-ng+wolfssl@aarch64-linux-musl@generic+v8a@fast+lto + PKG_HASH:=3fe0217615dd7060b7287d2b6b31d2a0b364137398bfb335a03bead322eac716 + else + PKG_ARCH:=chinadns-ng+wolfssl_noasm@aarch64-linux-musl@generic+v8a@fast+lto + PKG_HASH:=42ddd494200ec6d88b35902927688d316bc23e06e6c08d9e01eb2412196ab845 + endif +else ifeq ($(ARCH),arm) + ARM_CPU_FEATURES:=$(word 2,$(subst +,$(space),$(call qstrip,$(CONFIG_CPU_TYPE)))) + ifeq ($(ARM_CPU_FEATURES),) + PKG_ARCH:=chinadns-ng+wolfssl@arm-linux-musleabi@generic+v6+soft_float@fast+lto + PKG_HASH:=0a401d1dc11129481b2baf86f847d55d66bd7e725cba4bf57875fdad27ef0052 + else + PKG_ARCH:=chinadns-ng+wolfssl@arm-linux-musleabihf@generic+v7a@fast+lto + PKG_HASH:=dfa1f6ba80fb0925613822f4c4e00df8da68e7b8b772048d26a0d1a9d07d346b + endif +else ifeq ($(ARCH),mips) + PKG_ARCH:=chinadns-ng+wolfssl@mips-linux-musl@mips32+soft_float@fast+lto + PKG_HASH:=b610821a8f61b0ed3c8c7e82e10d401348a9de17f900988589024a37c4099c8e +else ifeq ($(ARCH),mipsel) + PKG_ARCH:=chinadns-ng+wolfssl@mipsel-linux-musl@mips32+soft_float@fast+lto + PKG_HASH:=760544a88724e3b1b9eac79c9400231e81aa8786f8f00a979229e175811ffe6d +else ifeq ($(ARCH),mips64) + PKG_ARCH:=chinadns-ng+wolfssl@mips64-linux-musl@mips64+soft_float@fast+lto + PKG_HASH:=2d0fce18a7ef1d74fdc12738767e66998a52c2b30d8790da760933853fe8726e +else ifeq ($(ARCH),i386) + PKG_ARCH:=chinadns-ng+wolfssl@i386-linux-musl@i686@fast+lto + PKG_HASH:=85e057dd0a0e8913b30471737436ab8b71834c494ed9f9e53544261b1ffdc8d6 +else ifeq ($(ARCH),x86_64) + PKG_ARCH:=chinadns-ng+wolfssl@x86_64-linux-musl@x86_64@fast+lto + PKG_HASH:=842ea4e9816efd91d39bc76ead5c4a42e79011757e37c521b4270b675cfcb30c +else + PKG_HASH:=dummy +endif + +PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION)-$(PKG_ARCH) +PKG_SOURCE_URL:=https://github.com/zfl9/chinadns-ng/releases/download/$(PKG_VERSION)/$(PKG_ARCH)? +UNPACK_CMD=$(CP) $(DL_DIR)/$(PKG_SOURCE) $(PKG_BUILD_DIR)/$(PKG_NAME) + +PKG_LICENSE:=AGPL-3.0-only +PKG_LICENSE_FILES:=LICENSE +PKG_MAINTAINER:=sbwml + +include $(INCLUDE_DIR)/package.mk + +define Package/chinadns-ng + SECTION:=net + CATEGORY:=Network + SUBMENU:=IP Addresses and Names + TITLE:=ChinaDNS next generation, refactoring with epoll and ipset. + URL:=https://github.com/zfl9/chinadns-ng + DEPENDS:=@(aarch64||arm||i386||mips||mipsel||mips64||x86_64) +ipset +endef + +define Build/Compile +endef + +define Package/chinadns-ng/install + $(INSTALL_DIR) $(1)/usr/bin + $(INSTALL_BIN) $(PKG_BUILD_DIR)/chinadns-ng $(1)/usr/bin +endef + +$(eval $(call BuildPackage,chinadns-ng)) diff --git a/ddns-go/Makefile b/ddns-go/Makefile new file mode 100644 index 000000000..293ef21f7 --- /dev/null +++ b/ddns-go/Makefile @@ -0,0 +1,58 @@ +# SPDX-License-Identifier: GPL-3.0-only +# +# Copyright (C) 2021-2023 sirpdboy +# +# This is free software, licensed under the Apache License, Version 2.0 . +# + +include $(TOPDIR)/rules.mk + +PKG_NAME:=ddns-go +PKG_VERSION:=6.14.1 +PKG_RELEASE:=1 + +PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz +PKG_SOURCE_URL:=https://codeload.github.com/jeessy2/ddns-go/tar.gz/v$(PKG_VERSION)? +PKG_HASH:=6a38c08e8c2fb17243720a94b0c54b8636019e7a4f151de60271d4ce19a41f48 + +PKG_LICENSE:=MIT +PKG_LICENSE_FILES:=LICENSE +PKG_MAINTAINER:=Tianling Shen + +PKG_BUILD_DEPENDS:=golang/host +PKG_BUILD_PARALLEL:=1 +PKG_BUILD_FLAGS:=no-mips16 + +GO_PKG:=github.com/jeessy2/ddns-go/v6 +GO_PKG_LDFLAGS_X:=main.version=$(PKG_VERSION) + +include $(INCLUDE_DIR)/package.mk +include $(TOPDIR)/feeds/packages/lang/golang/golang-package.mk + +define Package/ddns-go + TITLE:=A Linux web GUI client of ddns-go + SECTION:=net + CATEGORY:=Network + SUBMENU:=Web Servers/Proxies + DEPENDS:=$(GO_ARCH_DEPENDS) +ca-bundle + URL:=https://github.com/jeessy2/ddns-go + USERID:=ddns-go:ddns-go +endef + +define Package/ddns-go/description + ddns-go is a automatically obtain your public IPv4 or IPv6 address and resolve it to the corresponding domain name service, + support Alidns Dnspod Cloudflare Hicloud Callback Baiducloud porkbun GoDaddy Google Domains. +endef + +define Package/ddns-go/install + $(call GoPackage/Package/Install/Bin,$(1)) + + $(INSTALL_DIR) $(1)/etc/init.d + $(INSTALL_BIN) $(CURDIR)/file/ddns-go.init $(1)/etc/init.d/ddns-go + + $(INSTALL_DIR) $(1)/etc/uci-defaults + $(INSTALL_BIN) $(CURDIR)/file/luci-ddns-go.uci-default $(1)/etc/uci-defaults/luci-ddns-go +endef + +$(eval $(call GoBinPackage,ddns-go)) +$(eval $(call BuildPackage,ddns-go)) diff --git a/ddns-go/file/ddns-go.init b/ddns-go/file/ddns-go.init new file mode 100644 index 000000000..d845dc55d --- /dev/null +++ b/ddns-go/file/ddns-go.init @@ -0,0 +1,46 @@ +#!/bin/sh /etc/rc.common +# +# Copyright (C) 2021-2023 sirpdboy https://github.com/sirpdboy/luci-app-ddns-go +# +# This file is part of ddns-go . +# +# This is free software, licensed under the Apache License, Version 2.0 . +# + + +START=99 +USE_PROCD=1 + +PROG=/usr/bin/ddns-go +CONFDIR=/etc/ddns-go +CONF=$CONFDIR/ddns-go-config.yaml + +get_config() { + config_get_bool enabled $1 enabled 1 + config_get_bool logger $1 logger 1 + config_get port $1 port 9876 + config_get time $1 time 300 +} + +init_yaml(){ + [ -d $CONFDIR ] || mkdir -p $CONFDIR 2>/dev/null + cat /usr/share/ddns-go/ddns-go-default.yaml > $CONF +} + +start_service() { + config_load ddns-go + config_foreach get_config basic + [ x$enabled == x1 ] || return 1 + [ -s ${CONF} ] || init_yaml + logger -t ddns-go -p warn "ddns-go is start." + echo "ddns-go is start." + procd_open_instance + procd_set_param command $PROG -l :$port -f $time -c "$CONF" + [ "x$logger" == x1 ] && procd_set_param stderr 1 + procd_set_param respawn + procd_close_instance +} + +service_triggers() { + procd_add_reload_trigger "ddns-go" +} diff --git a/ddns-go/file/luci-ddns-go.uci-default b/ddns-go/file/luci-ddns-go.uci-default new file mode 100644 index 000000000..0c01cacc1 --- /dev/null +++ b/ddns-go/file/luci-ddns-go.uci-default @@ -0,0 +1,7 @@ +#!/bin/sh + +[ -s "/etc/ddns-go/localtime" ] && mv -f /etc/ddns-go/localtime /etc/localtime +/etc/init.d/ddns-go enable +/etc/init.d/ddns-go start +rm -f /tmp/luci* +exit 0 diff --git a/dns2socks-rust/Makefile b/dns2socks-rust/Makefile new file mode 100644 index 000000000..4a4749380 --- /dev/null +++ b/dns2socks-rust/Makefile @@ -0,0 +1,53 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Copyright (C) 2017-2024 Zxlhhyccc +# Copyright (C) 2021-2024 ImmortalWrt.org + +include $(TOPDIR)/rules.mk + +PKG_NAME:=dns2socks-rust +PKG_VERSION:=0.2.3 +PKG_RELEASE:=1 + +PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz +PKG_SOURCE_URL:=https://codeload.github.com/tun2proxy/dns2socks/tar.gz/v$(PKG_VERSION)? +PKG_HASH:=4bb4a238aace1ad2b2e8b8f7414a5d28838e380299da57379bae6254f642be42 +PKG_BUILD_DIR:=$(BUILD_DIR)/dns2socks-$(PKG_VERSION) + +PKG_MAINTAINER:=Zxlhhyccc +PKG_LICENSE:=MIT +PKG_LICENSE_FILES:=LICENSE + +PKG_BUILD_PARALLEL:=1 + +PKG_BUILD_DEPENDS:=rust/host +PKG_BUILD_PARALLEL:=1 + +#RUST_PKG:=dns2socks + +include $(INCLUDE_DIR)/package.mk +include $(TOPDIR)/feeds/packages/lang/rust/rust-package.mk + +define Package/dns2socks-rust + SECTION:=net + CATEGORY:=Network + SUBMENU:=IP Addresses and Names + TITLE:=DNS forwards to SOCKS5 server + URL:=https://github.com/tun2proxy/dns2socks.git + DEPENDS:=$$(RUST_ARCH_DEPENDS) +endef + +define Package/dns2socks-rust/description + This is a DNS server that forwards DNS requests to a SOCKS5 server. +endef + +define Build/Compile + $(call Build/Compile/Cargo,,--all-features) +endef + +define Package/dns2socks-rust/install + $(INSTALL_DIR) $(1)/usr/bin + $(INSTALL_BIN) $(PKG_BUILD_DIR)/target/$(RUSTC_TARGET_ARCH)/release/dns2socks $(1)/usr/bin/dns2socks-rust +endef + +$(eval $(call BuildPackage,dns2socks-rust)) diff --git a/dns2socks/Makefile b/dns2socks/Makefile new file mode 100644 index 000000000..47662fa09 --- /dev/null +++ b/dns2socks/Makefile @@ -0,0 +1,53 @@ +# SPDX-License-Identifier: GPL-3.0-only +# +# Copyright (C) 2021 ImmortalWrt.org + +include $(TOPDIR)/rules.mk + +PKG_NAME:=dns2socks +PKG_VERSION:=2.1 +PKG_RELEASE:=2 + +PKG_SOURCE:=SourceCode.zip +PKG_SOURCE_URL:=@SF/dns2socks +PKG_SOURCE_DATE:=2020-02-18 +PKG_HASH:=406b5003523577d39da66767adfe54f7af9b701374363729386f32f6a3a995f4 + +PKG_MAINTAINER:=ghostmaker +PKG_LICENSE:=BSD-3-Clause +PKG_LICENSE_FILE:=LICENSE + +include $(INCLUDE_DIR)/package.mk + +UNZIP_CMD:=unzip -q -d $(PKG_BUILD_DIR) $(DL_DIR)/$(PKG_SOURCE) + +define Package/dns2socks + SECTION:=net + CATEGORY:=Network + SUBMENU:=IP Addresses and Names + TITLE:=DNS to SOCKS or HTTP proxy + URL:=http://dns2socks.sourceforge.net/ + DEPENDS:=+libpthread +endef + +define Package/dns2socks/description + This is a command line utility to resolve DNS requests via + a SOCKS tunnel like Tor or a HTTP proxy. +endef + +define Build/Compile + $(TARGET_CC) \ + $(TARGET_CFLAGS) \ + $(TARGET_CPPFLAGS) \ + $(FPIC) \ + -o $(PKG_BUILD_DIR)/DNS2SOCKS/dns2socks \ + $(PKG_BUILD_DIR)/DNS2SOCKS/DNS2SOCKS.c \ + $(TARGET_LDFLAGS) -pthread +endef + +define Package/dns2socks/install + $(INSTALL_DIR) $(1)/usr/bin + $(INSTALL_BIN) $(PKG_BUILD_DIR)/DNS2SOCKS/dns2socks $(1)/usr/bin/dns2socks +endef + +$(eval $(call BuildPackage,dns2socks)) diff --git a/dns2tcp/Makefile b/dns2tcp/Makefile new file mode 100644 index 000000000..108d04ca5 --- /dev/null +++ b/dns2tcp/Makefile @@ -0,0 +1,42 @@ +# SPDX-License-Identifier: GPL-3.0-only +# +# Copyright (C) 2022 ImmortalWrt.org + +include $(TOPDIR)/rules.mk + +PKG_NAME:=dns2tcp +PKG_VERSION:=1.1.2 +PKG_RELEASE:=2 + +PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz +PKG_SOURCE_URL:=https://codeload.github.com/zfl9/dns2tcp/tar.gz/v$(PKG_VERSION)? +PKG_HASH:=5e8c6302a1d32c16ae7d4b8e39cd9aad1f2d7e68fe18813e76cb1e48ec5940d2 + +PKG_MAINTAINER:=Tianling Shen +PKG_LICENSE:=AGPL-3.0-only +PKG_LICENSE_FILES:=LICENSE + +PKG_BUILD_PARALLEL:=1 +PKG_BUILD_FLAGS:=no-mips16 gc-sections lto + +include $(INCLUDE_DIR)/package.mk + +define Package/dns2tcp + SECTION:=net + CATEGORY:=Network + SUBMENU:=IP Addresses and Names + TITLE:=utility to convert dns query from udp to tcp + URL:=https://github.com/zfl9/dns2tcp +endef + +TARGET_CFLAGS+= $(FPIC) +MAKE_FLAGS+= \ + CFLAGS="-std=c99 $(TARGET_CFLAGS)" \ + EVCFLAGS="$(TARGET_CFLAGS)" + +define Package/dns2tcp/install + $(INSTALL_DIR) $(1)/usr/bin + $(INSTALL_BIN) $(PKG_BUILD_DIR)/dns2tcp $(1)/usr/bin/ +endef + +$(eval $(call BuildPackage,dns2tcp)) diff --git a/docker/Makefile b/docker/Makefile new file mode 100644 index 000000000..eb7f77acb --- /dev/null +++ b/docker/Makefile @@ -0,0 +1,74 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=docker +PKG_VERSION:=29.2.0-rc.1 +PKG_RELEASE:=1 + +PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz +PKG_SOURCE_URL:=https://codeload.github.com/docker/cli/tar.gz/v$(PKG_VERSION)? +PKG_HASH:=d40a4567e1c8f76217a07a6f9010b570bdc257fef8d55c9671875bde166d9732 +PKG_BUILD_DIR:=$(BUILD_DIR)/cli-$(PKG_VERSION) +PKG_GIT_SHORT_COMMIT:=$(shell $(CURDIR)/git-short-commit.sh 'github.com/docker/cli' 'v$(PKG_VERSION)' '$(TMP_DIR)/git-short-commit/$(PKG_NAME)-$(PKG_VERSION)') + +PKG_MAINTAINER:=Gerard Ryan +PKG_LICENSE:=Apache-2.0 +PKG_LICENSE_FILES:=LICENSE + +PKG_BUILD_DEPENDS:=golang/host +PKG_BUILD_PARALLEL:=1 +PKG_BUILD_FLAGS:=no-mips16 + +GO_PKG:=github.com/docker/cli + +include $(INCLUDE_DIR)/package.mk +include $(TOPDIR)/feeds/packages/lang/golang/golang-package.mk + +define Package/docker + SECTION:=utils + CATEGORY:=Utilities + TITLE:=Docker Community Edition CLI + URL:=https://www.docker.com/ + DEPENDS:=$(GO_ARCH_DEPENDS) +endef + +define Package/docker/description +The CLI used in the Docker CE and Docker EE products. +endef + +GO_PKG_INSTALL_EXTRA:=\ + cli/compose/schema/data \ + vendor/google.golang.org/protobuf/internal/editiondefaults/editions_defaults.binpb + +TAR_OPTIONS:=--strip-components 1 $(TAR_OPTIONS) +TAR_CMD=$(HOST_TAR) -C $(1) $(TAR_OPTIONS) +TARGET_LDFLAGS += $(if $(CONFIG_USE_GLIBC),-lc -lgcc_eh) + +define Build/Prepare + $(Build/Prepare/Default) + + # Verify PKG_GIT_SHORT_COMMIT + ( \ + EXPECTED_PKG_GIT_SHORT_COMMIT=$$$$( $(CURDIR)/git-short-commit.sh 'github.com/docker/cli' 'v$(PKG_VERSION)' '$(TMP_DIR)/git-short-commit/$(PKG_NAME)-$(PKG_VERSION)' ); \ + if [ "$$$${EXPECTED_PKG_GIT_SHORT_COMMIT}" != "$(strip $(PKG_GIT_SHORT_COMMIT))" ]; then \ + echo "ERROR: Expected 'PKG_GIT_SHORT_COMMIT:=$$$${EXPECTED_PKG_GIT_SHORT_COMMIT}', found 'PKG_GIT_SHORT_COMMIT:=$(strip $(PKG_GIT_SHORT_COMMIT))'"; \ + exit 1; \ + fi \ + ) +endef + +define Build/Compile + ( \ + cd $(PKG_BUILD_DIR); \ + $(GO_PKG_VARS) \ + GITCOMMIT=$(PKG_GIT_SHORT_COMMIT) \ + VERSION=$(PKG_VERSION) \ + ./scripts/build/binary; \ + ) +endef + +define Package/docker/install + $(INSTALL_DIR) $(1)/usr/bin/ + $(INSTALL_BIN) $(PKG_BUILD_DIR)/build/docker $(1)/usr/bin/ +endef + +$(eval $(call BuildPackage,docker)) diff --git a/docker/git-short-commit.sh b/docker/git-short-commit.sh new file mode 100644 index 000000000..5edc7ff2e --- /dev/null +++ b/docker/git-short-commit.sh @@ -0,0 +1,49 @@ + +#!/bin/sh +# +# USAGE: git-short-commit.sh +# + +set -e + +error() { + echo "ERROR: ${*}" >&2 + exit 1 +} + +GIT_URL="${1}" +if [ -z "${GIT_URL}" ]; then + error "Git URL not specified" +fi + +GIT_REF="${2}" +if [ -z "${GIT_REF}" ]; then + error "Git reference not specified" +fi + +GIT_DIR="${3}" +if [ -z "${GIT_DIR}" ]; then + error "Git clone directory not specified" +fi + +clean_up() { + rm -rf "${GIT_DIR}" +} +trap clean_up EXIT + +git init --quiet "${GIT_DIR}" +( + cd "${GIT_DIR}" + for PREFIX in "" "https://" "http://" "git@"; do + echo "Trying remote '${PREFIX}${GIT_URL}'" >&2 + git remote add origin "${PREFIX}${GIT_URL}" + + if git fetch --depth 1 origin "${GIT_REF}"; then + git checkout --detach FETCH_HEAD -- + git rev-parse --short HEAD + break + fi + + git remote remove origin + done +) diff --git a/dockerd/Config.in b/dockerd/Config.in index d5763f051..2e7502a71 100644 --- a/dockerd/Config.in +++ b/dockerd/Config.in @@ -9,7 +9,7 @@ config DOCKER_CHECK_CONFIG config DOCKER_CGROUP_OPTIONS bool "Enable available kernel support for CGroupsV1" - default n + default y depends on PACKAGE_dockerd select KERNEL_CGROUP_DEVICE select KERNEL_CGROUP_FREEZER @@ -54,7 +54,7 @@ menu "Network" config DOCKER_NET_MACVLAN bool "Includes macvlan kernel modules" - default n + default y select PACKAGE_kmod-macvlan select PACKAGE_kmod-dummy @@ -75,7 +75,7 @@ menu "Storage" config DOCKER_STO_EXT4 bool "Enables support for ext3 or ext4 as the backing filesystem" - default n + default y select KERNEL_EXT4_FS_POSIX_ACL select KERNEL_EXT4_FS_SECURITY diff --git a/dockerd/Makefile b/dockerd/Makefile index 2a9558d8c..06095d9b5 100644 --- a/dockerd/Makefile +++ b/dockerd/Makefile @@ -1,8 +1,8 @@ include $(TOPDIR)/rules.mk PKG_NAME:=dockerd -PKG_VERSION:=27.3.1 -PKG_RELEASE:=3 +PKG_VERSION:=29.1.3 +PKG_RELEASE:=1 PKG_LICENSE:=Apache-2.0 PKG_LICENSE_FILES:=LICENSE @@ -10,8 +10,8 @@ PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz PKG_GIT_URL:=github.com/moby/moby PKG_GIT_REF:=v$(PKG_VERSION) PKG_SOURCE_URL:=https://codeload.$(PKG_GIT_URL)/tar.gz/$(PKG_GIT_REF)? -PKG_HASH:=d18208d9e0b6421307342cdef266193984c97c87177b9262b1113e6e9e7e020e -PKG_GIT_SHORT_COMMIT:=41ca978 # SHA1 used within the docker executables +PKG_HASH:=0e450c03c536a1304ba8fd26ca4c4ff96fac62182fd042fec90ffdf4a0969d40 +PKG_GIT_SHORT_COMMIT:=$(shell $(CURDIR)/git-short-commit.sh '$(PKG_GIT_URL)' '$(PKG_GIT_REF)' '$(TMP_DIR)/git-short-commit/$(PKG_NAME)-$(PKG_VERSION)') PKG_MAINTAINER:=Gerard Ryan @@ -71,7 +71,7 @@ define EnsureVendoredVersion DEP_VER=$$$$( grep --only-matching --perl-regexp '(?<=PKG_VERSION:=)(.*)' "$(1)" ); \ VEN_VER=$$$$( grep --only-matching --perl-regexp '(?<=_VERSION:=v)(.*)(?=})' "$(PKG_BUILD_DIR)/hack/dockerfile/install/$(2)" ); \ if [ "$$$${VEN_VER}" != "$$$${DEP_VER}" ]; then \ - echo "ERROR: $(PKG_NAME) Expected 'PKG_VERSION:=$$$${VEN_VER}' in '$(1)', found 'PKG_VERSION:=$$$${DEP_VER}'"; \ + echo "ERROR: Expected 'PKG_VERSION:=$$$${VEN_VER}' in '$(1)', found 'PKG_VERSION:=$$$${DEP_VER}'"; \ exit 1; \ fi \ ) diff --git a/dockerd/files/dockerd.init b/dockerd/files/dockerd.init index 8835a6d5f..111e2cd8d 100755 --- a/dockerd/files/dockerd.init +++ b/dockerd/files/dockerd.init @@ -150,7 +150,7 @@ ucidel() { } process_config() { - local alt_config_file data_root log_level iptables ip6tables bip + local alt_config_file data_root log_level iptables bip [ -f /etc/config/dockerd ] || { # Use the daemon default configuration @@ -172,7 +172,6 @@ process_config() { config_get data_root globals data_root "/opt/docker/" config_get log_level globals log_level "warn" config_get_bool iptables globals iptables "1" - config_get_bool ip6tables globals ip6tables "0" # Don't add these options by default # omission == docker defaults @@ -185,18 +184,12 @@ process_config() { config_get ip globals ip "" config_get fixed_cidr globals fixed_cidr "" config_get fixed_cidr_v6 globals fixed_cidr_v6 "" - # Use the *_proxy environment variable as the default value - config_get http_proxy proxies http_proxy "${http_proxy}" - config_get https_proxy proxies https_proxy "${https_proxy}" - config_get no_proxy proxies no_proxy "${no_proxy}" - config_get storage_driver globals storage_driver "" . /usr/share/libubox/jshn.sh json_init json_add_string "data-root" "${data_root}" json_add_string "log-level" "${log_level}" json_add_boolean "iptables" "${iptables}" - json_add_boolean "ip6tables" "${ip6tables}" [ -z "${log_driver}" ] || json_add_string "log-driver" "${log_driver}" [ -z "${bip}" ] || json_add_string "bip" "${bip}" [ -z "${registry_mirrors}" ] || json_add_array "registry-mirrors" @@ -212,14 +205,6 @@ process_config() { [ -z "${ip}" ] || json_add_string "ip" "${ip}" [ -z "${fixed_cidr}" ] || json_add_string "fixed-cidr" "${fixed_cidr}" [ -z "${fixed_cidr_v6}" ] || json_add_string "fixed-cidr-v6" "${fixed_cidr_v6}" - if [ -n "${http_proxy}" ] || [ -n "${https_proxy}" ] || [ -n "${no_proxy}" ]; then - json_add_object "proxies" - [ -z "${http_proxy}" ] || json_add_string "http-proxy" "${http_proxy}" - [ -z "${https_proxy}" ] || json_add_string "https-proxy" "${https_proxy}" - [ -z "${no_proxy}" ] || json_add_string "no-proxy" "${no_proxy}" - json_close_object - fi - [ -z "${storage_driver}" ] || json_add_string "storage-driver" "${storage_driver}" json_dump > "${DOCKERD_CONF}" [ "${iptables}" -eq "1" ] && config_foreach iptables_add_blocking_rule firewall diff --git a/dockerd/files/etc/config/dockerd b/dockerd/files/etc/config/dockerd index e3fde7caf..dd7523543 100644 --- a/dockerd/files/etc/config/dockerd +++ b/dockerd/files/etc/config/dockerd @@ -21,13 +21,6 @@ config globals 'globals' # list registry_mirrors 'https://' # list registry_mirrors 'https://hub.docker.com' -# If your organization uses a proxy server to connect to the internet, you may need to configure the proxy. -# See https://docs.docker.com/engine/daemon/proxy/ for more details -config proxies 'proxies' -# option http_proxy 'http://proxy.example.com:3128' -# option https_proxy 'https://proxy.example.com:3129' -# option no_proxy '*.test.example.com,.example.org,127.0.0.0/8' - # Docker doesn't work well out of the box with fw4. This is because Docker relies on a compatibility layer that # naively translates iptables rules. For the best compatibility replace the following dependencies: # `firewall4` -> `firewall` diff --git a/dockerd/git-short-commit.sh b/dockerd/git-short-commit.sh index 650ab8c82..5edc7ff2e 100755 --- a/dockerd/git-short-commit.sh +++ b/dockerd/git-short-commit.sh @@ -1,3 +1,4 @@ + #!/bin/sh # # USAGE: git-short-commit.sh diff --git a/filebrowser/Makefile b/filebrowser/Makefile new file mode 100644 index 000000000..46bc216e4 --- /dev/null +++ b/filebrowser/Makefile @@ -0,0 +1,68 @@ +# SPDX-License-Identifier: GPL-3.0-only +# +# Copyright (C) 2024 op.dllkids.xyz + +include $(TOPDIR)/rules.mk + +PKG_NAME:=filebrowser +PKG_VERSION:=2.52.0 +PKG_RELEASE:=1 + +PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz +PKG_SOURCE_URL:=https://codeload.github.com/filebrowser/filebrowser/tar.gz/v${PKG_VERSION}? +PKG_HASH:=f0a78ffe3f296b01992fe166b4191eddd7deea2e00b9449f748072391dff48a9 + +PKG_LICENSE:=Apache-2.0 +PKG_LICENSE_FILES:=LICENSE +PKG_MAINTAINER:=kenzo + +PKG_BUILD_DEPENDS:=golang/host node/host +PKG_BUILD_PARALLEL:=1 +PKG_USE_MIPS16:=0 +PKG_BUILD_FLAGS:=no-mips16 + +GO_PKG:=github.com/filebrowser/filebrowser +GO_PKG_LDFLAGS_X:= \ + $(GO_PKG)/v2/version.CommitSHA=$(PKG_SOURCE_VERSION) \ + $(GO_PKG)/v2/version.Version=v$(PKG_VERSION) + +include $(INCLUDE_DIR)/package.mk +include $(TOPDIR)/feeds/packages/lang/golang/golang-package.mk + +define Package/filebrowser + SECTION:=utils + CATEGORY:=Utilities + TITLE:=Web File Browser + URL:=https://github.com/filebrowser/filebrowser + DEPENDS:=$(GO_ARCH_DEPENDS) +endef + +define Package/filebrowser/description + filebrowser provides a file managing interface within a specified directory + and it can be used to upload, delete, preview, rename and edit your files. + It allows the creation of multiple users and each user can have its own directory. + It can be used as a standalone app or as a middleware. +endef + +define Build/Compile + ( \ + pushd "$(PKG_BUILD_DIR)/frontend" ; \ + npm ci; \ + npm run lint ; \ + npm run build ; \ + popd ; \ + $(call GoPackage/Build/Compile) ; \ + ) +endef + +define Package/filebrowser/install + $(call GoPackage/Package/Install/Bin,$(1)) + + $(INSTALL_DIR) $(1)/etc/config + $(INSTALL_CONF) $(CURDIR)/files/filebrowser.config $(1)/etc/config/filebrowser + $(INSTALL_DIR) $(1)/etc/init.d + $(INSTALL_BIN) $(CURDIR)/files/filebrowser.init $(1)/etc/init.d/filebrowser +endef + +$(eval $(call GoBinPackage,filebrowser)) +$(eval $(call BuildPackage,filebrowser)) \ No newline at end of file diff --git a/filebrowser/files/filebrowser.config b/filebrowser/files/filebrowser.config new file mode 100644 index 000000000..85eb6ac44 --- /dev/null +++ b/filebrowser/files/filebrowser.config @@ -0,0 +1,9 @@ + +config filebrowser 'config' + option addr_type 'lan' + option db_dir '/etc' + option db_name 'filebrowser.db' + option enabled '0' + option port '8989' + option root_dir '/' + diff --git a/filebrowser/files/filebrowser.init b/filebrowser/files/filebrowser.init new file mode 100755 index 000000000..dda404638 --- /dev/null +++ b/filebrowser/files/filebrowser.init @@ -0,0 +1,34 @@ +#!/bin/sh /etc/rc.common +# Copyright (C) 2021 ImmortalWrt + +START=90 +STOP=10 + +addr_type="$(uci get filebrowser.config.addr_type)" +db_dir="$(uci get filebrowser.config.db_dir)" +[ "${db_dir}" == "/" ] || db_dir="${db_dir%*/}" +db_name="$(uci get filebrowser.config.db_name| sed 's#/##g')" +enabled="$(uci get filebrowser.config.enabled)" +port="$(uci get filebrowser.config.port)" +root_dir="$(uci get filebrowser.config.root_dir)" + +if [ "${addr_type}" == "local" ];then + addr="127.0.0.1" +elif [ "${addr_type}" == "lan" ];then + addr="$(uci get network.lan.ipaddr)" +elif [ "${addr_type}" == "wan" ];then + addr="0.0.0.0" +fi + +start() { + stop + [ "$enabled" == "1" ] || exit 0 + mkdir -p "${root_dir}" + mkdir -p "${db_dir}" + filebrowser -a "${addr}" -d "${db_dir}/${db_name}" -p "${port}" -r "${root_dir}" >/dev/null 2>&1 & +} + +stop() { + echo "${db_dir}/${db_name}" > "/lib/upgrade/keep.d/filebrowser" + killall -3 filebrowser >/dev/null 2>&1 +} diff --git a/geoview/Makefile b/geoview/Makefile new file mode 100644 index 000000000..e27576390 --- /dev/null +++ b/geoview/Makefile @@ -0,0 +1,48 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=geoview +PKG_VERSION:=0.1.11 +PKG_RELEASE:=1 + +PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz +PKG_SOURCE_URL:=https://codeload.github.com/snowie2000/geoview/tar.gz/$(PKG_VERSION)? +PKG_HASH:=a3ad07d3926c329f6990d67e17119f0c9a4ee26e89b0e2f541b27230c2806e94 + +PKG_LICENSE:=Apache-2.0 +PKG_LICENSE_FILES:=LICENSE +PKG_MAINTAINER:=snowie2000 + +PKG_BUILD_DEPENDS:=golang/host +PKG_BUILD_PARALLEL:=1 +PKG_USE_MIPS16:=0 +PKG_BUILD_FLAGS:=no-mips16 + +GO_PKG:=github.com/snowie2000/geoview +GO_PKG_BUILD_PKG:=$(GO_PKG) + +GO_PKG_LDFLAGS:=-s -w + +include $(INCLUDE_DIR)/package.mk +include $(TOPDIR)/feeds/packages/lang/golang/golang-package.mk + +define Package/geoview + TITLE:=A geofile toolkit + URL:=https://github.com/snowie2000/geoview + SECTION:=net + CATEGORY:=Network + SUBMENU:=IP Addresses and Names + DEPENDS+= $(GO_ARCH_DEPENDS) +endef + +define Package/geoview/description + geoview is a handy tool to extract useful information from geo* files. +endef + +define Package/geoview/install + $(call GoPackage/Package/Install/Bin,$(PKG_INSTALL_DIR)) + $(INSTALL_DIR) $(1)/usr/bin + $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/geoview $(1)/usr/bin/ +endef + +$(eval $(call GoBinPackage,geoview)) +$(eval $(call BuildPackage,geoview)) diff --git a/gn/Makefile b/gn/Makefile index 001c4bfe4..5263ebdfd 100644 --- a/gn/Makefile +++ b/gn/Makefile @@ -9,9 +9,9 @@ PKG_RELEASE:=1 PKG_SOURCE_PROTO:=git PKG_SOURCE_URL:=https://gn.googlesource.com/gn.git -PKG_SOURCE_DATE:=2025-11-07 -PKG_SOURCE_VERSION:=e7f3202128bdb2429872fdb138626a010b2bff7f -PKG_MIRROR_HASH:=4f3f373f1c2f64cf00c7fcbf6da86f759289b369a9a15d437fb5b01a7e17ec9e +PKG_SOURCE_DATE:=2025-12-21 +PKG_SOURCE_VERSION:=64d35867ca0a1088f13de8f4ccaf1a5687d7f1ce +PKG_MIRROR_HASH:=0fc81f9f57c89030264fdf8d65f3473e8c353f4e6a9096226a4a818ad4fbdf8e PKG_LICENSE:=BSD 3-Clause PKG_LICENSE_FILES:=LICENSE diff --git a/gn/src/out/last_commit_position.h b/gn/src/out/last_commit_position.h index 92713e0f0..6d1453308 100644 --- a/gn/src/out/last_commit_position.h +++ b/gn/src/out/last_commit_position.h @@ -3,7 +3,7 @@ #ifndef OUT_LAST_COMMIT_POSITION_H_ #define OUT_LAST_COMMIT_POSITION_H_ -#define LAST_COMMIT_POSITION_NUM 2292 -#define LAST_COMMIT_POSITION "2292 (e7f3202128bd)" +#define LAST_COMMIT_POSITION_NUM 2312 +#define LAST_COMMIT_POSITION "2312 (64d35867ca0a)" #endif // OUT_LAST_COMMIT_POSITION_H_ diff --git a/gost/Makefile b/gost/Makefile new file mode 100644 index 000000000..3ef8e0738 --- /dev/null +++ b/gost/Makefile @@ -0,0 +1,56 @@ +# SPDX-License-Identifier: GPL-3.0-only +# +# Copyright (C) 2021 ImmortalWrt.org + +include $(TOPDIR)/rules.mk + +PKG_NAME:=gost +PKG_VERSION:=3.2.6 +PKG_RELEASE:=1 + +PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz +PKG_SOURCE_URL:=https://codeload.github.com/go-gost/gost/tar.gz/v$(PKG_VERSION)? +PKG_HASH:=79874354530b899576dd4866d3b1400651d0b17c1e7a90ad30c44686a0642600 + +PKG_LICENSE:=MIT +PKG_LICENSE_FILES:=LICENSE +PKG_MAINTAINER:=Tianling Shen + +PKG_BUILD_DEPENDS:=golang/host +PKG_BUILD_PARALLEL:=1 +PKG_BUILD_FLAGS:=no-mips16 + +GO_PKG:=github.com/go-gost/gost +GO_PKG_BUILD_PKG:=$(GO_PKG)/cmd/gost +GO_PKG_LDFLAGS_X:=main.version=$(PKG_VERSION) + +include $(INCLUDE_DIR)/package.mk +include $(TOPDIR)/feeds/packages/lang/golang/golang-package.mk + +define Package/gost + SECTION:=net + CATEGORY:=Network + TITLE:=GO Simple Tunnel + URL:=https://gost.run/ + DEPENDS:=$(GO_ARCH_DEPENDS) +ca-bundle + USERID:=gost:gost +endef + +define Package/gost/conffiles +/etc/config/gost +/etc/gost/ +endef + +define Package/gost/install + $(call GoPackage/Package/Install/Bin,$(1)) + + $(INSTALL_DIR) $(1)/etc/config + $(INSTALL_CONF) $(CURDIR)/files/gost.config $(1)/etc/config/gost + $(INSTALL_DIR) $(1)/etc/init.d + $(INSTALL_BIN) $(CURDIR)/files/gost.init $(1)/etc/init.d/gost + $(INSTALL_DIR) $(1)/etc/uci-defaults + $(INSTALL_BIN) $(CURDIR)/files/gost.uci $(1)/etc/uci-defaults/99-gost-migrate +endef + +$(eval $(call GoBinPackage,gost)) +$(eval $(call BuildPackage,gost)) diff --git a/gost/files/gost.config b/gost/files/gost.config new file mode 100644 index 000000000..129445bad --- /dev/null +++ b/gost/files/gost.config @@ -0,0 +1,6 @@ + +config gost 'config' + option enabled '0' + option config_file '' + #list arguments '' + diff --git a/gost/files/gost.init b/gost/files/gost.init new file mode 100644 index 000000000..3a0e5b774 --- /dev/null +++ b/gost/files/gost.init @@ -0,0 +1,46 @@ +#!/bin/sh /etc/rc.common + +USE_PROCD=1 +START=99 + +CONF="gost" +PROG="/usr/bin/gost" + +start_service() { + local enabled + + config_load "gost" + config_get_bool enabled "config" "enabled" 0 + [ "$enabled" -eq 1 ] || return 1 + + local config_file arguments + config_get config_file "config" "config_file" + config_get arguments "config" "arguments" + + procd_open_instance + + procd_set_param command "$PROG" + if [ -n "$config_file" ]; then + procd_append_param command -C "$config_file" + procd_set_param file "$config_file" + fi + if [ -n "$arguments" ]; then + set -- $arguments + procd_append_param command "$@" + fi + + procd_set_param user gost + procd_set_param group gost + + procd_set_param limits core="unlimited" + procd_set_param limits nofile="1000000 1000000" + procd_set_param stderr 1 + + procd_set_param respawn + + procd_close_instance +} + +service_triggers() { + procd_add_reload_trigger "$CONF" +} diff --git a/gost/files/gost.uci b/gost/files/gost.uci new file mode 100644 index 000000000..b8389cb6a --- /dev/null +++ b/gost/files/gost.uci @@ -0,0 +1,10 @@ +#!/bin/sh + +grep -q "config gost$" "/etc/config/gost" || exit 0 + +sed -e "s,config gost,config gost 'config'," \ + -e "s,option enable ,option enabled ," \ + -e "s,option run_command,list arguments," \ + -i "/etc/config/gost" + +exit 0 diff --git a/hysteria/Makefile b/hysteria/Makefile new file mode 100644 index 000000000..b1711be3c --- /dev/null +++ b/hysteria/Makefile @@ -0,0 +1,59 @@ +# SPDX-License-Identifier: GPL-3.0-only +# +# Copyright (C) 2021 ImmortalWrt.org + +include $(TOPDIR)/rules.mk + +PKG_NAME:=hysteria +PKG_VERSION:=2.6.5 +PKG_RELEASE:=1 + +PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz +PKG_SOURCE_URL:=https://codeload.github.com/apernet/hysteria/tar.gz/app/v$(PKG_VERSION)? +PKG_HASH:=21a04ef8ce640d7c60c3b8678500b6e6481862d9af62f9ce2663b772211718d0 +PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-app-v$(PKG_VERSION) + +PKG_LICENSE:=MIT +PKG_LICENSE_FILES:=LICENSE +PKG_MAINTAINER:=Tianling Shen + +PKG_BUILD_DEPENDS:=golang/host +PKG_BUILD_PARALLEL:=1 +PKG_USE_MIPS16:=0 +PKG_BUILD_FLAGS:=no-mips16 + +GO_PKG:=github.com/apernet/hysteria +GO_PKG_BUILD_PKG:=$(GO_PKG)/app/v2 +GO_PKG_LDFLAGS_X = \ + $(GO_PKG)/app/v2/cmd.appVersion=v$(PKG_VERSION) \ + $(GO_PKG)/app/v2/cmd.appType=release \ + $(GO_PKG)/app/v2/cmd.appPlatform=$(GO_OS) \ + $(GO_PKG)/app/v2/cmd.appArch=$(GO_ARCH) + +include $(INCLUDE_DIR)/package.mk +include $(TOPDIR)/feeds/packages/lang/golang/golang-package.mk + +define Package/hysteria + SECTION:=net + CATEGORY:=Network + TITLE:=A feature-packed network utility optimized for networks of poor quality + URL:=https://github.com/tobyxdd/hysteria + DEPENDS:=$(GO_ARCH_DEPENDS) +ca-bundle +endef + +define Package/hysteria/description + Hysteria is a feature-packed network utility optimized for networks + of poor quality (e.g. satellite connections, congested public Wi-Fi, + connecting from China to servers abroad) powered by a custom version + of QUIC protocol. +endef + +define Package/hysteria/install + $(call GoPackage/Package/Install/Bin,$(PKG_INSTALL_DIR)) + + $(INSTALL_DIR) $(1)/usr/bin + $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/app $(1)/usr/bin/hysteria +endef + +$(eval $(call GoBinPackage,hysteria)) +$(eval $(call BuildPackage,hysteria)) diff --git a/ipt2socks/Makefile b/ipt2socks/Makefile new file mode 100644 index 000000000..806766776 --- /dev/null +++ b/ipt2socks/Makefile @@ -0,0 +1,46 @@ +# SPDX-License-Identifier: GPL-3.0-only +# +# Copyright (C) 2021 ImmortalWrt.org + +include $(TOPDIR)/rules.mk + +PKG_NAME:=ipt2socks +PKG_VERSION:=1.1.4 +PKG_RELEASE:=3 + +PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz +PKG_SOURCE_URL:=https://codeload.github.com/zfl9/ipt2socks/tar.gz/v$(PKG_VERSION)? +PKG_HASH:=68dc76e63951d655c2fd9b420e175b5a75a50014d6db6e729398b41f2c988356 + +PKG_BUILD_PARALLEL:=1 +PKG_USE_MIPS16:=0 +PKG_BUILD_FLAGS:=no-mips16 + +PKG_LICENSE:=AGPL-3.0 +PKG_LICENSE_FILE:=LICENSE + +include $(INCLUDE_DIR)/package.mk + +define Package/ipt2socks + SECTION:=net + CATEGORY:=Network + TITLE:=Convert iptables to socks5 + URL:=https://github.com/zfl9/ipt2socks + DEPENDS:=+libpthread +endef + +define Package/ipt2socks/description + Utility for converting iptables (redirect/tproxy) to socks5. +endef + +TARGET_CFLAGS+= $(FPIC) -flto +MAKE_FLAGS+= \ + CFLAGS="-std=c99 -pthread $(TARGET_CFLAGS)" \ + EVCFLAGS="$(TARGET_CFLAGS)" + +define Package/ipt2socks/install + $(INSTALL_DIR) $(1)/usr/bin + $(INSTALL_BIN) $(PKG_BUILD_DIR)/ipt2socks $(1)/usr/bin +endef + +$(eval $(call BuildPackage,ipt2socks)) diff --git a/lua-neturl/Makefile b/lua-neturl/Makefile new file mode 100644 index 000000000..246dcf710 --- /dev/null +++ b/lua-neturl/Makefile @@ -0,0 +1,44 @@ +# SPDX-License-Identifier: GPL-3.0-only +# +# Copyright (C) 2022-2023 ImmortalWrt.org + +include $(TOPDIR)/rules.mk + +PKG_NAME:=neturl +PKG_VERSION:=1.2-1 +PKG_RELEASE:=1 + +PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz +PKG_SOURCE_URL:=https://codeload.github.com/golgote/neturl/tar.gz/v$(PKG_VERSION)? +PKG_HASH:=fc4ea1b114125ae821bef385936cd12429485204576e77b6283692bd0cc9b1ab + +PKG_MAINTAINER:=Tianling Shen +PKG_LICENSE:=MIT +PKG_LICNESE_FILES:=LICENSE.txt + +include $(INCLUDE_DIR)/package.mk + +define Package/lua-neturl + SUBMENU:=Lua + SECTION:=lang + CATEGORY:=Languages + TITLE:=URL and Query string parser, builder, normalizer for Lua + URL:=https://github.com/golgote/neturl + DEPENDS:=+lua + PKGARCH:=all +endef + +define Package/lua-neturl/description + This small Lua library provides a few functions to parse URL with + querystring and build new URL easily. +endef + +define Build/Compile +endef + +define Package/lua-neturl/install + $(INSTALL_DIR) $(1)/usr/lib/lua + $(CP) $(PKG_BUILD_DIR)/lib/net/url.lua $(1)/usr/lib/lua/ +endef + +$(eval $(call BuildPackage,lua-neturl)) diff --git a/lua-neturl/patches/010-userinfo-regex.patch b/lua-neturl/patches/010-userinfo-regex.patch new file mode 100644 index 000000000..9dbd91ccf --- /dev/null +++ b/lua-neturl/patches/010-userinfo-regex.patch @@ -0,0 +1,20 @@ +--- a/lib/net/url.lua ++++ b/lib/net/url.lua +@@ -340,7 +340,7 @@ function M:setAuthority(authority) + self.password = v + return '' + end) +- if string.find(userinfo, "^[%w%+%.]+$") then ++ if string.find(userinfo, "^[%p%w%+%.]+$") then + self.user = userinfo + else + -- incorrect userinfo +@@ -369,7 +369,7 @@ function M.parse(url) + comp.fragment = v + return '' + end) +- url =url:gsub('^([%w][%w%+%-%.]*)%:', function(v) ++ url =url:gsub('^([%w][%w%+%-%_%.]*)%:', function(v) + comp.scheme = v:lower() + return '' + end) diff --git a/luci-app-autoupdate/Makefile b/luci-app-autoupdate/Makefile new file mode 100755 index 000000000..86c991168 --- /dev/null +++ b/luci-app-autoupdate/Makefile @@ -0,0 +1,15 @@ +# Copyright (C) 2020-2022 Hyy2001X + +include $(TOPDIR)/rules.mk + +PKG_NAME:=luci-app-autoupdate +PKG_VERSION:=9 +PKG_RELEASE:=3 + +LUCI_TITLE:=LuCI Support for autoupdate +LUCI_DEPENDS:=+autoupdate +LUCI_PKGARCH:=all + +include $(TOPDIR)/feeds/luci/luci.mk + +# call BuildPackage - OpenWrt buildroot signature diff --git a/luci-app-autoupdate/README.md b/luci-app-autoupdate/README.md new file mode 100755 index 000000000..6ec0d26fc --- /dev/null +++ b/luci-app-autoupdate/README.md @@ -0,0 +1,3 @@ +# luci-app-autoupdate + +此项目需要搭配 [AutoBuild-Actions](https://github.com/Hyy2001X/AutoBuild-Actions) 使用 diff --git a/luci-app-autoupdate/luasrc/controller/autoupdate.lua b/luci-app-autoupdate/luasrc/controller/autoupdate.lua new file mode 100755 index 000000000..fdf594e70 --- /dev/null +++ b/luci-app-autoupdate/luasrc/controller/autoupdate.lua @@ -0,0 +1,17 @@ +module("luci.controller.autoupdate",package.seeall) + +function index() + if not nixio.fs.access("/etc/config/autoupdate") then + return + end + entry({"admin", "system", "autoupdate"}, alias("admin", "system", "autoupdate", "main"), _("AutoUpdate"), 99).dependent = true + entry({"admin", "system", "autoupdate", "main"}, cbi("autoupdate/main"), _("Scheduled Upgrade"), 10).leaf = true + entry({"admin", "system", "autoupdate", "manual"}, cbi("autoupdate/manual"), _("Manually Upgrade"), 20).leaf = true + entry({"admin", "system", "autoupdate", "log"}, form("autoupdate/log"), _("Upgrade Log"), 30).leaf = true + entry({"admin", "system", "autoupdate", "print_log"}, call("print_log")).leaf = true +end + +local logfile = luci.sys.exec("autoupdate --env Log_Full") +function print_log() + luci.http.write(luci.sys.exec("tail -n 50 " .. logfile .. " 2> /dev/null")) +end \ No newline at end of file diff --git a/luci-app-autoupdate/luasrc/model/cbi/autoupdate/log.lua b/luci-app-autoupdate/luasrc/model/cbi/autoupdate/log.lua new file mode 100755 index 000000000..fb6aec327 --- /dev/null +++ b/luci-app-autoupdate/luasrc/model/cbi/autoupdate/log.lua @@ -0,0 +1,6 @@ +log = SimpleForm("autoupdate") +log.reset = false +log.submit = false +log:append(Template("autoupdate/autoupdate_log")) + +return log diff --git a/luci-app-autoupdate/luasrc/model/cbi/autoupdate/main.lua b/luci-app-autoupdate/luasrc/model/cbi/autoupdate/main.lua new file mode 100755 index 000000000..06c69c2a6 --- /dev/null +++ b/luci-app-autoupdate/luasrc/model/cbi/autoupdate/main.lua @@ -0,0 +1,80 @@ +m = Map("autoupdate", translate("AutoUpdate"), +translate("AutoUpdate LUCI supports scheduled upgrade & one-click firmware upgrade") +.. [[

]] +.. translate("Powered by AutoBuild-Actions") +.. [[]] +) + +s = m:section(TypedSection, "autoupdate") +s.anonymous = true + +local default_url = luci.sys.exec("autoupdate --env Github") +local default_flag = luci.sys.exec("autoupdate --env TARGET_FLAG") +local default_logpath = luci.sys.exec("autoupdate --env Log_Path") + +enable = s:option(Flag, "enable", translate("Enable"), translate("Automatically update firmware during the specified time when Enabled")) +enable.default = 0 +enable.optional = false + +proxy = s:option(Flag, "proxy", translate("Preference Mirror Speedup"), translate("Preference Mirror for speeding up download")) +proxy.default = 1 +proxy:depends("enable", "1") +proxy.optional = false + +proxy_type = s:option(ListValue, "proxy_type", translate("Mirror Station")) +proxy_type.default = "A" +proxy_type:value("A", translate("Automatic selection (Recommend)")) +proxy_type:value("G", translate("GitHub Proxy")) +proxy_type:value("F", translate("CF Workers")) +proxy_type:depends("proxy", "1") +proxy_type.optional = false + +advanced = s:option(Flag, "advanced", translate("Advanced Settings")) +advanced.default = 0 +advanced:depends("enable", "1") + +advanced_settings = s:option(MultiValue, "advanced_settings", translate("Advanced Settings"), translate("Supported Multi Selection")) +advanced_settings:value("--skip-verify", translate("Skip SHA256 Verify")) +advanced_settings:value("-F", translate("Force Flash Firmware")) +advanced_settings:value("--decompress", translate("Decompress [img.gz] Firmware")) +advanced_settings:value("-n", translate("Upgrade without keeping config")) +advanced_settings:depends("advanced", "1") +advanced.description = translate("Please don't select it unless you know what you're doing!") + +week = s:option(ListValue, "week", translate("Update Day"), translate("Recommend to set the AUTOUPDATE time to an uncommon time")) +week:value(7, translate("Everyday")) +week:value(1, translate("Monday")) +week:value(2, translate("Tuesday")) +week:value(3, translate("Wednesday")) +week:value(4, translate("Thursday")) +week:value(5, translate("Friday")) +week:value(6, translate("Saturday")) +week:value(0, translate("Sunday")) +week.default = 0 +week:depends("enable", "1") + +hour = s:option(Value, "hour", translate("Hour")) +hour.datatype = "range(0,23)" +hour.rmempty = true +hour.default = 0 +hour:depends("enable", "1") + +minute = s:option(Value, "minute", translate("Minute")) +minute.datatype = "range(0,59)" +minute.rmempty = true +minute.default = 30 +minute:depends("enable", "1") + +github = s:option(Value, "github", translate("Github Url"), translate("For detecting cloud version and downloading firmware")) +github.default = default_url +github.rmempty = false + +flag = s:option(Value, "flag", translate("Firmware Flag")) +flag.default = default_flag +flag.rmempty = false + +logpath = s:option(Value, "logpath", translate("Log Path")) +logpath.default = default_logpath +logpath.rmempty = false + +return m diff --git a/luci-app-autoupdate/luasrc/model/cbi/autoupdate/manual.lua b/luci-app-autoupdate/luasrc/model/cbi/autoupdate/manual.lua new file mode 100755 index 000000000..d51b8724e --- /dev/null +++ b/luci-app-autoupdate/luasrc/model/cbi/autoupdate/manual.lua @@ -0,0 +1,47 @@ +m = Map("autoupdate",translate("Manually Upgrade"),translate("Manually upgrade Firmware or Script")) +s = m:section(TypedSection,"autoupdate") +s.anonymous = true + +local local_version = luci.sys.exec ("autoupdate -V") +local local_script_version = luci.sys.exec ("autoupdate -v") + +check_updates = s:option (Button, "_check_updates", translate("Check Updates"),translate("Please wait for the page to refresh after clicking Check Updates button")) +check_updates.inputtitle = translate ("Check Updates") +check_updates.write = function() + luci.sys.call ("autoupdate -V Cloud > /tmp/Cloud_Version") + luci.sys.call ("autoupdate -v Cloud > /tmp/Cloud_Script_Version") + luci.http.redirect(luci.dispatcher.build_url("admin", "system", "autoupdate", "manual")) +end + +local cloud_version = luci.sys.exec ("cat /tmp/Cloud_Version 2> /dev/null") +local cloud_script_version = luci.sys.exec ("cat /tmp/Cloud_Script_Version 2> /dev/null") + +upgrade_fw = s:option (Button, "_upgrade_fw", translate("Upgrade Firmware"),translate("Upgrade Normally (KEEP CONFIG)") .. "

当前固件版本: " .. local_version .. "
云端固件版本: " .. cloud_version) +upgrade_fw.inputtitle = translate ("Do Upgrade") +upgrade_fw.write = function() + luci.sys.call ("autoupdate -u > /dev/null &") + luci.http.redirect(luci.dispatcher.build_url("admin", "system", "autoupdate", "log")) +end + +upgrade_fw_force = s:option (Button, "_upgrade_fw_force", translate("Upgrade Firmware"),translate("Upgrade with Force Flashing (DANGEROUS)")) +upgrade_fw_force.inputtitle = translate ("Do Upgrade") +upgrade_fw_force.write = function() + luci.sys.call ("autoupdate -u -F > /dev/null &") + luci.http.redirect(luci.dispatcher.build_url("admin", "system", "autoupdate", "log")) +end + +upgrade_fw_n = s:option (Button, "_upgrade_fw_n", translate("Upgrade Firmware"),translate("Upgrade without keeping System-Config")) +upgrade_fw_n.inputtitle = translate ("Do Upgrade") +upgrade_fw_n.write = function() + luci.sys.call ("autoupdate -u -n > /dev/null &") + luci.http.redirect(luci.dispatcher.build_url("admin", "system", "autoupdate", "log")) +end + +upgrade_script = s:option (Button, "_upgrade_script", translate("Upgrade Script"),translate("Using the latest Script may solve some compatibility problems") .. "

当前脚本版本: " .. local_script_version .. "
云端脚本版本: " .. cloud_script_version) +upgrade_script.inputtitle = translate ("Do Upgrade") +upgrade_script.write = function() + luci.sys.call ("autoupdate -x -P > /dev/null &") + luci.http.redirect(luci.dispatcher.build_url("admin", "system", "autoupdate", "log")) +end + +return m diff --git a/luci-app-autoupdate/luasrc/view/autoupdate/autoupdate_log.htm b/luci-app-autoupdate/luasrc/view/autoupdate/autoupdate_log.htm new file mode 100755 index 000000000..dc0fa2554 --- /dev/null +++ b/luci-app-autoupdate/luasrc/view/autoupdate/autoupdate_log.htm @@ -0,0 +1,20 @@ +<% local module = require "luci.dispatcher" -%> + + +
+ +
diff --git a/luci-app-autoupdate/po/zh-cn/autoupdate.po b/luci-app-autoupdate/po/zh-cn/autoupdate.po new file mode 100755 index 000000000..8424323b7 --- /dev/null +++ b/luci-app-autoupdate/po/zh-cn/autoupdate.po @@ -0,0 +1,104 @@ +msgid "AutoUpdate" +msgstr "固件更新" + +msgid "AutoUpdate LUCI supports scheduled upgrade & one-click firmware upgrade" +msgstr "固件更新 LUCI 支持定时更新固件 & 一键更新固件" + +msgid "Scheduled Upgrade" +msgstr "定时更新" + +msgid "Manually Upgrade" +msgstr "手动更新" + +msgid "Manually upgrade Firmware or Script" +msgstr "手动更新固件或脚本" + +msgid "Upgrade Log" +msgstr "更新日志" + +msgid "Preference Mirror Speedup" +msgstr "镜像加速" + +msgid "Mirror Station" +msgstr "镜像站" + +msgid "Automatic selection (Recommend)" +msgstr "自动选择 (推荐)" + +msgid "GitHub Proxy" +msgstr "GitHub Proxy - Ghproxy" + +msgid "CF Workers" +msgstr "Cloudflare Workers" + +msgid "Preference Mirror for speeding up download" +msgstr "优先使用镜像站加速下载" + +msgid "Advanced Settings" +msgstr "高级设置" + +msgid "Supported Multi Selection" +msgstr "支持同时勾选多个选项" + +msgid "Skip SHA256 Verify" +msgstr "跳过固件 SHA256 校验" + +msgid "Force Flash Firmware" +msgstr "强制刷写固件" + +msgid "Decompress [img.gz] Firmware" +msgstr "解压 [img.gz] 格式固件" + +msgid "Upgrade without keeping config" +msgstr "不保留配置" + +msgid "Please don't select it unless you know what you're doing!" +msgstr "请勿随意勾选,除非你知道自己在做什么!" + +msgid "Recommend to set the AUTOUPDATE time to an uncommon time" +msgstr "建议设置定时更新时间为设备不常用的时间" + +msgid "Update Day" +msgstr "更新时间" + +msgid "Github Url" +msgstr "Github 地址" + +msgid "For detecting cloud version and downloading firmware" +msgstr "用于检测固件版本更新以及下载固件的地址" + +msgid "Firmware Flag" +msgstr "固件标签" + +msgid "Log Path" +msgstr "日志路径" + +msgid "Do Upgrade" +msgstr "执行更新" + +msgid "Upgrade Firmware" +msgstr "更新固件" + +msgid "Automatically update firmware during the specified time when Enabled" +msgstr "启用后,将在指定时间段自动检查并更新固件" + +msgid "Upgrade without keeping System-Config" +msgstr "更新固件 (不保留配置)" + +msgid "Upgrade with Force Flashing (DANGEROUS)" +msgstr "更新固件且强制刷入固件 (危险)" + +msgid "Upgrade Normally (KEEP CONFIG)" +msgstr "更新固件 (保留配置)" + +msgid "Using the latest Script may solve some compatibility problems" +msgstr "使用最新脚本也许能解决一些兼容性问题" + +msgid "Upgrade Script" +msgstr "更新脚本" + +msgid "Check Updates" +msgstr "检查更新" + +msgid "Please wait for the page to refresh after clicking Check Updates button" +msgstr "点击检查更新按钮后请等待页面自动刷新" diff --git a/luci-app-autoupdate/po/zh_Hans/autoupdate.po b/luci-app-autoupdate/po/zh_Hans/autoupdate.po new file mode 100755 index 000000000..8424323b7 --- /dev/null +++ b/luci-app-autoupdate/po/zh_Hans/autoupdate.po @@ -0,0 +1,104 @@ +msgid "AutoUpdate" +msgstr "固件更新" + +msgid "AutoUpdate LUCI supports scheduled upgrade & one-click firmware upgrade" +msgstr "固件更新 LUCI 支持定时更新固件 & 一键更新固件" + +msgid "Scheduled Upgrade" +msgstr "定时更新" + +msgid "Manually Upgrade" +msgstr "手动更新" + +msgid "Manually upgrade Firmware or Script" +msgstr "手动更新固件或脚本" + +msgid "Upgrade Log" +msgstr "更新日志" + +msgid "Preference Mirror Speedup" +msgstr "镜像加速" + +msgid "Mirror Station" +msgstr "镜像站" + +msgid "Automatic selection (Recommend)" +msgstr "自动选择 (推荐)" + +msgid "GitHub Proxy" +msgstr "GitHub Proxy - Ghproxy" + +msgid "CF Workers" +msgstr "Cloudflare Workers" + +msgid "Preference Mirror for speeding up download" +msgstr "优先使用镜像站加速下载" + +msgid "Advanced Settings" +msgstr "高级设置" + +msgid "Supported Multi Selection" +msgstr "支持同时勾选多个选项" + +msgid "Skip SHA256 Verify" +msgstr "跳过固件 SHA256 校验" + +msgid "Force Flash Firmware" +msgstr "强制刷写固件" + +msgid "Decompress [img.gz] Firmware" +msgstr "解压 [img.gz] 格式固件" + +msgid "Upgrade without keeping config" +msgstr "不保留配置" + +msgid "Please don't select it unless you know what you're doing!" +msgstr "请勿随意勾选,除非你知道自己在做什么!" + +msgid "Recommend to set the AUTOUPDATE time to an uncommon time" +msgstr "建议设置定时更新时间为设备不常用的时间" + +msgid "Update Day" +msgstr "更新时间" + +msgid "Github Url" +msgstr "Github 地址" + +msgid "For detecting cloud version and downloading firmware" +msgstr "用于检测固件版本更新以及下载固件的地址" + +msgid "Firmware Flag" +msgstr "固件标签" + +msgid "Log Path" +msgstr "日志路径" + +msgid "Do Upgrade" +msgstr "执行更新" + +msgid "Upgrade Firmware" +msgstr "更新固件" + +msgid "Automatically update firmware during the specified time when Enabled" +msgstr "启用后,将在指定时间段自动检查并更新固件" + +msgid "Upgrade without keeping System-Config" +msgstr "更新固件 (不保留配置)" + +msgid "Upgrade with Force Flashing (DANGEROUS)" +msgstr "更新固件且强制刷入固件 (危险)" + +msgid "Upgrade Normally (KEEP CONFIG)" +msgstr "更新固件 (保留配置)" + +msgid "Using the latest Script may solve some compatibility problems" +msgstr "使用最新脚本也许能解决一些兼容性问题" + +msgid "Upgrade Script" +msgstr "更新脚本" + +msgid "Check Updates" +msgstr "检查更新" + +msgid "Please wait for the page to refresh after clicking Check Updates button" +msgstr "点击检查更新按钮后请等待页面自动刷新" diff --git a/luci-app-autoupdate/root/etc/autoupdate/custom b/luci-app-autoupdate/root/etc/autoupdate/custom new file mode 100755 index 000000000..c2aa48120 --- /dev/null +++ b/luci-app-autoupdate/root/etc/autoupdate/custom @@ -0,0 +1,8 @@ +## 请在下方输入你的自定义变量,一行填写一个变量 +## 该文件将在运行 autoupdate 脚本时被读取, 且该文件中的内容优先级高于 default +# +## 示例(添加时不含注释): +# Author=Hyy2001 +# TARGET_PROFILE=x86_64 +# Github=https://github.com/Hyy2001X/AutoBuild-Actions +# \ No newline at end of file diff --git a/luci-app-autoupdate/root/etc/config/autoupdate b/luci-app-autoupdate/root/etc/config/autoupdate new file mode 100755 index 000000000..f360536c6 --- /dev/null +++ b/luci-app-autoupdate/root/etc/config/autoupdate @@ -0,0 +1,2 @@ +config autoupdate + option enable '0' diff --git a/luci-app-autoupdate/root/etc/init.d/autoupdate b/luci-app-autoupdate/root/etc/init.d/autoupdate new file mode 100755 index 000000000..5ef0154ab --- /dev/null +++ b/luci-app-autoupdate/root/etc/init.d/autoupdate @@ -0,0 +1,61 @@ +#!/bin/sh /etc/rc.common + +START=99 +LOGGER="logger -t [AutoUpdate]" + +Script_File="$(command -v autoupdate)" +Script_Cmd="$Script_File -u" + +start() { + local basic_list="enable proxy proxy_type advanced_settings github flag logpath week minute hour" + for i in $(echo $basic_list);do + local eval $i="$(uci_get_by_type autoupdate 0 $i)" + done;unset i + if [ ! "${Script_File}" ] + then + ${LOGGER} "Unable to access autoupdate,exit ..." + stop + exit 1 + else + chmod 777 ${Script_File} 2> /dev/null + fi + if [ "$enable" == 1 ] + then + [ "$week" == 7 ] && week='*' + [ "$proxy" == 1 ] && Script_Cmd="$Script_Cmd -P $proxy_type" + Script_Cmd="$Script_Cmd $advanced_settings" + ${LOGGER} "Creating corn_task [$minute $hour * * $week $Script_Cmd] ..." + echo "$minute $hour * * $week $Script_Cmd ## AutoUpdate crontab" >> /etc/crontabs/root + /etc/init.d/cron restart + else + ${LOGGER} "AutoUpdate Service is disabled ..." + stop + fi + ${LOGGER} "Setting Github URL to $github ..." + ${LOGGER} "Setting Flag to $flag ..." + ${LOGGER} "Setting Log Path to $logpath ..." + $Script_File -C $github > /dev/null 2>&1 + $Script_File --flag $flag > /dev/null 2>&1 + $Script_File --log -path $logpath > /dev/null 2>&1 +} + +stop() { + ${LOGGER} "Removing all corn tasks ..." + sed -i '/## AutoUpdate crontab/d' /etc/crontabs/root 2> /dev/null + /etc/init.d/cron restart +} + +disable() { + ${LOGGER} "Closing AutoUpdate Service ..." + uci set autoupdate.@autoupdate[0].enable="0" 2> /dev/null + stop +} + +service_triggers() { + procd_add_reload_trigger "autoupdate" +} + +uci_get_by_type() { + local ret=$(uci get autoupdate.@$1[$2].$3 2>/dev/null) + echo ${ret:=$4} +} diff --git a/luci-app-autoupdate/root/etc/uci-defaults/luci-autoupdate b/luci-app-autoupdate/root/etc/uci-defaults/luci-autoupdate new file mode 100755 index 000000000..ca487028c --- /dev/null +++ b/luci-app-autoupdate/root/etc/uci-defaults/luci-autoupdate @@ -0,0 +1,10 @@ +#!/bin/sh + +uci -q batch <<-EOF >/dev/null + delete ucitrack.@autoupdate[-1] + add ucitrack autoupdate + set ucitrack.@autoupdate[-1].init=autoupdate + commit ucitrack +EOF + +exit 0 \ No newline at end of file diff --git a/luci-app-autoupdate/root/usr/share/rpcd/acl.d/luci-app-autoupdate.json b/luci-app-autoupdate/root/usr/share/rpcd/acl.d/luci-app-autoupdate.json new file mode 100644 index 000000000..86d43bdda --- /dev/null +++ b/luci-app-autoupdate/root/usr/share/rpcd/acl.d/luci-app-autoupdate.json @@ -0,0 +1,11 @@ +{ + "luci-app-autoupdate": { + "description": "Grant UCI access for luci-app-autoupdate", + "read": { + "uci": [ "autoupdate" ] + }, + "write": { + "uci": [ "autoupdate" ] + } + } +} diff --git a/luci-app-dnsfilter/Makefile b/luci-app-dnsfilter/Makefile new file mode 100644 index 000000000..4080a42a4 --- /dev/null +++ b/luci-app-dnsfilter/Makefile @@ -0,0 +1,21 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=luci-app-dnsfilter +PKG_VERSION:=1.0 +PKG_RELEASE:=14 + +PKG_LICENSE:=GPLv2 +PKG_MAINTAINER:=small_5 kiddin9 + +LUCI_TITLE:=LuCI support for DNSFilter +LUCI_PKGARCH:=all +LUCI_DEPENDS:=+curl +dnsmasq-full +ipset + +define Package/$(PKG_NAME)/conffiles +/etc/config/dnsfilter +/etc/dnsfilter/ +endef + +include $(TOPDIR)/feeds/luci/luci.mk + +# call BuildPackage - OpenWrt buildroot signature diff --git a/luci-app-dnsfilter/README.md b/luci-app-dnsfilter/README.md new file mode 100644 index 000000000..c8e117799 --- /dev/null +++ b/luci-app-dnsfilter/README.md @@ -0,0 +1,18 @@ +基于 [small_5](https://github.com/small-5) 的 luci-app-adblock-plus 修改 + +# 基于DNS的广告过滤 for OpenWrt +## 功能 + +- 支持 AdGuardHome/Host/DNSMASQ/Domain 格式的规则订阅 + +- 规则自动识别, 自动去重, 定时更新 + +- 自定义黑白名单 + +- 短视频APP拦截 + +- 安全搜索 + +## 编译说明 + +本app依赖于```dnsmasq-full```,与OpenWrt默认的```dnsmasq```冲突,所以编译时请确保已经取消勾选```base-system -> dnsmasq``` diff --git a/luci-app-dnsfilter/luasrc/controller/dnsfilter.lua b/luci-app-dnsfilter/luasrc/controller/dnsfilter.lua new file mode 100644 index 000000000..9ceb78d5c --- /dev/null +++ b/luci-app-dnsfilter/luasrc/controller/dnsfilter.lua @@ -0,0 +1,51 @@ +module("luci.controller.dnsfilter", package.seeall) + +function index() + if not nixio.fs.access("/etc/config/dnsfilter") then + return + end + + local page = entry({"admin", "services", "dnsfilter"}, alias("admin", "services", "dnsfilter", "base"), _("DNS Filter"), 9) + page.dependent = true + page.acl_depends = { "luci-app-dnsfilter" } + + entry({"admin", "services", "dnsfilter", "base"}, cbi("dnsfilter/base"), _("Base Setting"), 10).leaf = true + entry({"admin", "services", "dnsfilter", "white"}, form("dnsfilter/white"), _("White Domain List"), 20).leaf = true + entry({"admin", "services", "dnsfilter", "black"}, form("dnsfilter/black"), _("Block Domain List"), 30).leaf = true + entry({"admin", "services", "dnsfilter", "ip"}, form("dnsfilter/ip"), _("Block IP List"), 40).leaf = true + entry({"admin", "services", "dnsfilter", "log"}, form("dnsfilter/log"), _("Update Log"), 50).leaf = true + entry({"admin", "services", "dnsfilter", "run"}, call("act_status")).leaf = true + entry({"admin", "services", "dnsfilter", "refresh"}, call("refresh_data")) +end + +function act_status() + local e = {} + e.running = luci.sys.call("[ -s /tmp/dnsmasq.dnsfilter/rules.conf ]") == 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 + + luci.sys.exec("/usr/share/dnsfilter/dnsfilter down") + icount = luci.sys.exec("find /tmp/ad_tmp -type f -name rules.conf -exec cat {} \\; 2>/dev/null | wc -l") + if tonumber(icount)>0 then + oldcount = luci.sys.exec("find /tmp/dnsfilter -type f -name rules.conf -exec cat {} \\; 2>/dev/null | wc -l") + if tonumber(icount) ~= tonumber(oldcount) then + luci.sys.exec("[ -h /tmp/dnsfilter/url ] && (rm -f /etc/dnsfilter/rules/*;cp -a /tmp/ad_tmp/* /etc/dnsfilter/rules) || (rm -f /tmp/dnsfilter/*;cp -a /tmp/ad_tmp/* /tmp/dnsfilter)") + luci.sys.exec("/etc/init.d/dnsfilter restart &") + retstring = tostring(math.ceil(tonumber(icount))) + else + retstring = 0 + end + luci.sys.call("echo `date +'%Y-%m-%d %H:%M:%S'` > /tmp/dnsfilter/dnsfilter.updated") + else + retstring = "-1" + end + luci.sys.exec("rm -rf /tmp/ad_tmp") + + luci.http.prepare_content("application/json") + luci.http.write_json({ret=retstring,retcount=icount}) +end diff --git a/luci-app-dnsfilter/luasrc/model/cbi/dnsfilter/base.lua b/luci-app-dnsfilter/luasrc/model/cbi/dnsfilter/base.lua new file mode 100644 index 000000000..cb0f85a0a --- /dev/null +++ b/luci-app-dnsfilter/luasrc/model/cbi/dnsfilter/base.lua @@ -0,0 +1,84 @@ +local SYS = require "luci.sys" + +m = Map("dnsfilter") +m.title = translate("DNS Filter") +m.description = translate("Support AdGuardHome/Host/DNSMASQ/Domain Rules") +m:section(SimpleSection).template = "dnsfilter/dnsfilter_status" + +s = m:section(TypedSection, "dnsfilter") +s.anonymous = true + +o = s:option(Flag, "enable", translate("Enable")) +o.rmempty = false + +o = s:option(Flag, "block_ios", translate("Block Apple iOS OTA update")) +o.rmempty = false +o.default = 0 + +o = s:option(Flag, "block_cnshort", translate("Block CNshort APP and Website")) +o.rmempty = false +o.default = 0 + +o = s:option(Flag, "safe_search", translate("Safe Search")) +o.description = translate("Enforcing SafeSearch for Google Bing Duckduckgo Yandex and Youtube.") +o.rmempty = false +o.default = 0 + +o = s:option(Flag, "cron_mode", translate("Enable automatic update rules")) +o.rmempty = false +o.default = 1 + +o = s:option(ListValue, "time_update", translate("Update time (every day)")) +for s = 0,23 do +o:value(s, s .. ':00') +end +o.default = 6 +o:depends("cron_mode",1) + +tmp_rule = 0 +if nixio.fs.access("/tmp/dnsfilter/rules.conf") then +tmp_rule = 1 +UD = SYS.exec("cat /tmp/dnsfilter/dnsfilter.updated 2>/dev/null") +rule_count = tonumber(SYS.exec("find /tmp/dnsfilter -exec cat {} \\; 2>/dev/null | wc -l")) +o = s:option(DummyValue, "1", translate("Subscribe Rules Data")) +o.rawhtml = true +o.template = "dnsfilter/dnsfilter_refresh" +o.value = rule_count.." "..translate("Records") +o.description = string.format(translate("AdGuardHome / Host / DNSMASQ / Domain rules auto-convert").."
"..translate("Last Update Checked")..": %s
",UD) +end + +o = s:option(Flag, "flash", translate("Save rules to flash")) +o.description = translate("Should be enabled when rules addresses are slow to download") +o.rmempty = false +o.default = 0 + +if tmp_rule == 1 then +o = s:option(Button, "delete", translate("Delete All Subscribe Rules")) +o.inputstyle = "reset" +o.description = translate("Delete rules files and delete the subscription link
There is no need to click for modify the subscription link,The script will automatically replace the old rule file") +o.write = function() + SYS.exec("[ -d /etc/dnsfilter/rules ] && rm -rf /etc/dnsfilter/rules") + SYS.exec("grep -wq 'list url' /etc/config/dnsfilter && sed -i '/list url/d' /etc/config/dnsfilter && /etc/init.d/dnsfilter restart 2>&1 &") + luci.http.redirect(luci.dispatcher.build_url("admin", "services", "dnsfilter", "base")) +end +end + +if luci.sys.call("[ -h /tmp/dnsfilter/url ] || exit 9") == 9 then + if nixio.fs.access("/etc/dnsfilter/rules") then + o = s:option(Button, "delete_1", translate("Delete Subscribe Rules On The Flash")) + o.inputstyle = "reset" + o.write = function() + SYS.exec("rm -rf /etc/dnsfilter/rules") + luci.http.redirect(luci.dispatcher.build_url("admin", "services", "dnsfilter", "base")) + end + end +end + +o = s:option(DynamicList, "url", translate("Anti-AD Rules Subscribe")) +o:value("https://anti-ad.net/anti-ad-for-dnsmasq.conf", translate("anti-AD")) +o:value("https://adguardteam.github.io/AdGuardSDNSFilter/Filters/filter.txt", translate("AdGuard")) +o:value("https://easylist-downloads.adblockplus.org/easylistchina+easylist.txt", translate("Easylistchina+Easylist")) +o:value("https://block.energized.pro/extensions/porn-lite/formats/domains.txt", translate("Anti-Porn")) +o.default = "https://anti-ad.net/anti-ad-for-dnsmasq.conf" + +return m diff --git a/luci-app-dnsfilter/luasrc/model/cbi/dnsfilter/black.lua b/luci-app-dnsfilter/luasrc/model/cbi/dnsfilter/black.lua new file mode 100644 index 000000000..43648645b --- /dev/null +++ b/luci-app-dnsfilter/luasrc/model/cbi/dnsfilter/black.lua @@ -0,0 +1,26 @@ +local fs = require "nixio.fs" +local conffile = "/etc/dnsfilter/black.list" + +f = SimpleForm("custom") +t = f:field(TextValue, "conf") +t.rmempty = true +t.rows = 13 +t.description = translate("Will Always block these Domain") + +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")) + else + luci.sys.call("> /etc/dnsfilter/black.list") + end + luci.sys.exec("[ \"$(uci -q get dnsfilter.@dnsfilter[0].enable)\" = 1 ] && /etc/init.d/dnsfilter restart") + end + return true +end + +return f diff --git a/luci-app-dnsfilter/luasrc/model/cbi/dnsfilter/ip.lua b/luci-app-dnsfilter/luasrc/model/cbi/dnsfilter/ip.lua new file mode 100644 index 000000000..b021df320 --- /dev/null +++ b/luci-app-dnsfilter/luasrc/model/cbi/dnsfilter/ip.lua @@ -0,0 +1,26 @@ +local fs = require "nixio.fs" +local conffile = "/etc/dnsfilter/ip.list" + +f = SimpleForm("custom") +t = f:field(TextValue, "conf") +t.rmempty = true +t.rows = 13 +t.description = translate("Will Always block these IP") + +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")) + else + luci.sys.call("> /etc/dnsfilter/ip.list") + end + luci.sys.exec("ipset -F blockip 2>/dev/null && for i in $(cat /etc/dnsfilter/ip.list);do ipset add blockip $i 2>/dev/null;done") + end + return true +end + +return f diff --git a/luci-app-dnsfilter/luasrc/model/cbi/dnsfilter/log.lua b/luci-app-dnsfilter/luasrc/model/cbi/dnsfilter/log.lua new file mode 100644 index 000000000..0bdd9f4d2 --- /dev/null +++ b/luci-app-dnsfilter/luasrc/model/cbi/dnsfilter/log.lua @@ -0,0 +1,16 @@ +local fs = require "nixio.fs" +local conffile = "/tmp/adupdate.log" + +f = SimpleForm("logview") +f.reset = false +f.submit = false +t = f:field(TextValue, "conf") +t.rmempty = true +t.rows = 20 + +function t.cfgvalue() + return fs.readfile(conffile) or "" +end +t.readonly = "readonly" + +return f diff --git a/luci-app-dnsfilter/luasrc/model/cbi/dnsfilter/white.lua b/luci-app-dnsfilter/luasrc/model/cbi/dnsfilter/white.lua new file mode 100644 index 000000000..949ffd36a --- /dev/null +++ b/luci-app-dnsfilter/luasrc/model/cbi/dnsfilter/white.lua @@ -0,0 +1,28 @@ +local fs = require "nixio.fs" +local conffile = "/etc/dnsfilter/white.list" + +f = SimpleForm("custom") +t = f:field(TextValue, "conf") +t.rmempty = true +t.rows = 13 +t.description = translate("Will Never filter these Domain") + +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")) + else + luci.sys.call("> /etc/dnsfilter/white.list") + end + luci.sys.exec("for i in $(cat /etc/dnsfilter/white.list);do sed -i -e \"/\\/$i\\//d\" -e \"/\\.$i\\//d\" /tmp/dnsfilter/rules.conf 2>/dev/null;\\\ + [ -s /etc/dnsfilter/rules/rules.conf ] && sed -i -e \"/\\/$i\\//d\" -e \"/\\.$i\\//d\" /etc/dnsfilter/rules/rules.conf;done;\\\ + [ -s /tmp/dnsfilter/rules.conf ] && rm -f /tmp/dnsmasq.dnsfilter/rules.conf && /etc/init.d/dnsfilter start") + end + return true +end + +return f diff --git a/luci-app-dnsfilter/luasrc/view/dnsfilter/dnsfilter_refresh.htm b/luci-app-dnsfilter/luasrc/view/dnsfilter/dnsfilter_refresh.htm new file mode 100644 index 000000000..2909bdc13 --- /dev/null +++ b/luci-app-dnsfilter/luasrc/view/dnsfilter/dnsfilter_refresh.htm @@ -0,0 +1,34 @@ +<%+cbi/valueheader%> + + +<%=self.value%> +<%+cbi/valuefooter%> diff --git a/luci-app-dnsfilter/luasrc/view/dnsfilter/dnsfilter_status.htm b/luci-app-dnsfilter/luasrc/view/dnsfilter/dnsfilter_status.htm new file mode 100644 index 000000000..02e974d2c --- /dev/null +++ b/luci-app-dnsfilter/luasrc/view/dnsfilter/dnsfilter_status.htm @@ -0,0 +1,21 @@ + + +
+

+ <%:Collecting data...%> +

+
diff --git a/luci-app-dnsfilter/po/zh-cn/dnsfilter.po b/luci-app-dnsfilter/po/zh-cn/dnsfilter.po new file mode 100644 index 000000000..800b3d4d6 --- /dev/null +++ b/luci-app-dnsfilter/po/zh-cn/dnsfilter.po @@ -0,0 +1,107 @@ +msgid "Base Setting" +msgstr "基本设置" + +msgid "DNS Filter" +msgstr "DNS 过滤器" + +msgid "Support AdGuardHome/Host/DNSMASQ/Domain Rules" +msgstr "支持 AdGuardHome/Host/DNSMASQ/Domain 规则" + +msgid "RUNNING" +msgstr "运行中" + +msgid "NOT RUNNING" +msgstr "未运行" + +msgid "Enable" +msgstr "启用" + +msgid "Block Apple iOS OTA update" +msgstr "拦截 Apple iOS 的OTA 更新" + +msgid "Block CNshort APP and Website" +msgstr "拦截 短视频 APP 和网站" + +msgid "Enable automatic update rules" +msgstr "启用规则自动更新" + +msgid "Update time (every day)" +msgstr "更新时间 (每天)" + +msgid "DNSFilter Data" +msgstr "DNSFilter 规则数据库" + +msgid "Records" +msgstr "条记录" + +msgid "Refresh..." +msgstr "正在更新,请稍候.." + +msgid "No new data!" +msgstr "你已经是最新数据,无需更新!" + +msgid "Refresh Error!" +msgstr "更新失败!" + +msgid "Refresh OK!" +msgstr "更新成功!" + +msgid "Total Records:" +msgstr "新的总纪录数:" + +msgid "Refresh Data" +msgstr "更新数据库" + +msgid "Last Update Checked" +msgstr "上一次检查规则更新" + +msgid "Subscribe Rules Data" +msgstr "去广告规则" + +msgid "AdGuardHome / Host / DNSMASQ / Domain rules auto-convert" +msgstr "AdGuardHome / Host / DNSMASQ / Domain 规则自动识别, 自动去重" + +msgid "Save rules to flash" +msgstr "保存规则到闪存" + +msgid "Should be enabled when rules addresses are slow to download" +msgstr "规则地址下载速度慢时应该打开" + +msgid "Delete All Subscribe Rules" +msgstr "清空所有订阅的规则库" + +msgid "Delete rules files and delete the subscription link
There is no need to click for modify the subscription link,The script will automatically replace the old rule file" +msgstr "清空规则文件,并且删除所有的订阅链接
如果只是修改订阅链接不需要清空,脚本会自动覆盖旧的规则文件" + +msgid "Delete Subscribe Rules On The Flash" +msgstr "清空闪存上的订阅规则库" + +msgid "Anti-AD Rules Subscribe" +msgstr "广告过滤规则订阅 URL" + +msgid "White Domain List" +msgstr "域名白名单" + +msgid "Will Never filter these Domain" +msgstr "永不过滤白名单内的域名" + +msgid "Block Domain List" +msgstr "域名黑名单" + +msgid "Will Always block these Domain" +msgstr "拦截黑名单内的域名" + +msgid "Block IP List" +msgstr "IP 黑名单" + +msgid "Will Always block these IP" +msgstr "拦截黑名单内的 IP 地址" + +msgid "Safe Search" +msgstr "安全搜索" + +msgid "Enforcing SafeSearch for Google Bing Duckduckgo Yandex and Youtube." +msgstr "强制为 Google Bing Duckduckgo Yandex Youtube 开启安全搜索,过滤不健康内容" + +msgid "Update Log" +msgstr "更新日志" diff --git a/luci-app-dnsfilter/po/zh_Hans b/luci-app-dnsfilter/po/zh_Hans new file mode 120000 index 000000000..41451e4a1 --- /dev/null +++ b/luci-app-dnsfilter/po/zh_Hans @@ -0,0 +1 @@ +zh-cn \ No newline at end of file diff --git a/luci-app-dnsfilter/root/etc/config/dnsfilter b/luci-app-dnsfilter/root/etc/config/dnsfilter new file mode 100644 index 000000000..f5f9f1892 --- /dev/null +++ b/luci-app-dnsfilter/root/etc/config/dnsfilter @@ -0,0 +1,6 @@ + +config dnsfilter + option cron_mode '1' + option time_update '6' + option flash '0' + option enable '0' diff --git a/luci-app-dnsfilter/root/etc/dnsfilter/black.list b/luci-app-dnsfilter/root/etc/dnsfilter/black.list new file mode 100644 index 000000000..e69de29bb diff --git a/luci-app-dnsfilter/root/etc/dnsfilter/ip.list b/luci-app-dnsfilter/root/etc/dnsfilter/ip.list new file mode 100644 index 000000000..e69de29bb diff --git a/luci-app-dnsfilter/root/etc/dnsfilter/white.list b/luci-app-dnsfilter/root/etc/dnsfilter/white.list new file mode 100644 index 000000000..ffbdf5be5 --- /dev/null +++ b/luci-app-dnsfilter/root/etc/dnsfilter/white.list @@ -0,0 +1 @@ +boxer.baidu.com diff --git a/luci-app-dnsfilter/root/etc/init.d/dnsfilter b/luci-app-dnsfilter/root/etc/init.d/dnsfilter new file mode 100755 index 000000000..5efe77482 --- /dev/null +++ b/luci-app-dnsfilter/root/etc/init.d/dnsfilter @@ -0,0 +1,449 @@ +#!/bin/sh /etc/rc.common +START=99 +STOP=10 +B=0 +CRON_FILE=/etc/crontabs/root +D=/tmp/dnsmasq.dnsfilter +E="date +'%Y-%m-%d %H:%M:%S'" +L=/etc/dnsfilter +P=/usr/share/dnsfilter +T=/tmp/dnsfilter +STATUS=Y + DEFAULT_DNSMASQ_CFGID="$(uci -q show "dhcp.@dnsmasq[0]" | awk 'NR==1 {split($0, conf, /[.=]/); print conf[2]}')" + if [ -f "/tmp/etc/dnsmasq.conf.$DEFAULT_DNSMASQ_CFGID" ]; then + DNSMASQ_CONF_DIR="$(awk -F '=' '/^conf-dir=/ {print $2}' "/tmp/etc/dnsmasq.conf.$DEFAULT_DNSMASQ_CFGID")" + if [ -n "$DNSMASQ_CONF_DIR" ]; then + DNSMASQ_CONF_DIR=${DNSMASQ_CONF_DIR%*/} + else + DNSMASQ_CONF_DIR="/tmp/dnsmasq.d" + fi + fi +TAG="_DNSFILTER_RULE_" +FWI=$(uci -q get firewall.dnsfilter.path) +enable=$(uci -q get dnsfilter.@dnsfilter[0].enable) +flash=$(uci -q get dnsfilter.@dnsfilter[0].flash) +url=$(uci -q get dnsfilter.@dnsfilter[0].url) + +get_config(){ + config_get_bool cron_mode $1 cron_mode 1 + config_get_bool block_ios $1 block_ios 0 + config_get_bool block_cnshort $1 block_cnshort 0 + config_get_bool safe_search $1 safe_search 0 + config_get time_update $1 time_update 6 +} + +add_dns(){ + mkdir -p $DNSMASQ_CONF_DIR $D + echo conf-dir=$D > $DNSMASQ_CONF_DIR/dnsfilter.conf + if [ -n "$url" -a ! -s /tmp/dnsfilter/failed ];then + mkdir -p $T + if [ $flash = 1 ];then + ln -sf $L/rules/rules.conf $T/rules.conf + ln -sf $L/rules/url $T/url + fi + ln -sf $T/rules.conf $D/rules.conf + fi + [ $block_ios = 1 ] && echo 'mesu.apple.com' > $D/black.conf + if [ $block_cnshort = 1 ];then + cat <<-EOF >> $D/black.conf +amemv.com +tiktokv.com +snssdk.com +douyin.com +ixigua.com +pstatp.com +ixiguavideo.com +v.kandian.qq.com +yximgs.com +gifshow.com +ksapisrv.com +kuaishoupay.com +ksyun.com +live.xycdn.com +danuoyi.alicdn.com +v.weishi.qq.com +pearvideo.com +miaopai.com +kuaishou.com +qupai.me +meipai.com +huoshan.com +ergengtv.com +baijiahao.baidu.com +xiongzhang.baidu.com +EOF + fi + cat $L/black.list >> $D/black.conf + if [ -s $D/black.conf ];then + sed -i -e 's:^:address=/:' -e 's:$:/:' $D/black.conf + echo "`sort -u $D/black.conf`" > $D/black.conf + for i in $(cat $D/black.conf);do + if grep -wq $i $D/rules.conf 2>/dev/null;then + sed -i -e "s#$i##" -e '/^$/d' $D/black.conf + fi + done + for i in $(cat $L/white.list);do sed -i -e "/\/$i\//d" -e "/\.$i\//d" $D/black.conf;done + else + rm -f $D/black.conf + fi + + if [ $safe_search = 1 ];then + cat <<-EOF >> $D/safesearch.conf +address=/www.bing.com/204.79.197.220 +address=/www.google.com/216.239.38.120 +address=/google.com/216.239.38.120 +address=/www.google.ad/216.239.38.120 +address=/google.ad/216.239.38.120 +address=/www.google.ae/216.239.38.120 +address=/google.ae/216.239.38.120 +address=/www.google.al/216.239.38.120 +address=/google.al/216.239.38.120 +address=/www.google.am/216.239.38.120 +address=/google.am/216.239.38.120 +address=/www.google.as/216.239.38.120 +address=/google.as/216.239.38.120 +address=/www.google.at/216.239.38.120 +address=/google.at/216.239.38.120 +address=/www.google.az/216.239.38.120 +address=/google.az/216.239.38.120 +address=/www.google.ba/216.239.38.120 +address=/google.ba/216.239.38.120 +address=/www.google.be/216.239.38.120 +address=/google.be/216.239.38.120 +address=/www.google.bf/216.239.38.120 +address=/google.bf/216.239.38.120 +address=/www.google.bg/216.239.38.120 +address=/google.bg/216.239.38.120 +address=/www.google.bi/216.239.38.120 +address=/google.bi/216.239.38.120 +address=/www.google.bj/216.239.38.120 +address=/google.bj/216.239.38.120 +address=/www.google.bs/216.239.38.120 +address=/google.bs/216.239.38.120 +address=/www.google.bt/216.239.38.120 +address=/google.bt/216.239.38.120 +address=/www.google.by/216.239.38.120 +address=/google.by/216.239.38.120 +address=/www.google.ca/216.239.38.120 +address=/google.ca/216.239.38.120 +address=/www.google.cd/216.239.38.120 +address=/google.cd/216.239.38.120 +address=/www.google.cf/216.239.38.120 +address=/google.cf/216.239.38.120 +address=/www.google.cg/216.239.38.120 +address=/google.cg/216.239.38.120 +address=/www.google.ch/216.239.38.120 +address=/google.ch/216.239.38.120 +address=/www.google.ci/216.239.38.120 +address=/google.ci/216.239.38.120 +address=/www.google.cl/216.239.38.120 +address=/google.cl/216.239.38.120 +address=/www.google.cm/216.239.38.120 +address=/google.cm/216.239.38.120 +address=/www.google.cn/216.239.38.120 +address=/google.cn/216.239.38.120 +address=/www.google.cv/216.239.38.120 +address=/google.cv/216.239.38.120 +address=/www.google.cz/216.239.38.120 +address=/google.cz/216.239.38.120 +address=/www.google.de/216.239.38.120 +address=/google.de/216.239.38.120 +address=/www.google.dj/216.239.38.120 +address=/google.dj/216.239.38.120 +address=/www.google.dk/216.239.38.120 +address=/google.dk/216.239.38.120 +address=/www.google.dm/216.239.38.120 +address=/google.dm/216.239.38.120 +address=/www.google.dz/216.239.38.120 +address=/google.dz/216.239.38.120 +address=/www.google.ee/216.239.38.120 +address=/google.ee/216.239.38.120 +address=/www.google.es/216.239.38.120 +address=/google.es/216.239.38.120 +address=/www.google.fi/216.239.38.120 +address=/google.fi/216.239.38.120 +address=/www.google.fm/216.239.38.120 +address=/google.fm/216.239.38.120 +address=/www.google.fr/216.239.38.120 +address=/google.fr/216.239.38.120 +address=/www.google.ga/216.239.38.120 +address=/google.ga/216.239.38.120 +address=/www.google.ge/216.239.38.120 +address=/google.ge/216.239.38.120 +address=/www.google.gg/216.239.38.120 +address=/google.gg/216.239.38.120 +address=/www.google.gl/216.239.38.120 +address=/google.gl/216.239.38.120 +address=/www.google.gm/216.239.38.120 +address=/google.gm/216.239.38.120 +address=/www.google.gr/216.239.38.120 +address=/google.gr/216.239.38.120 +address=/www.google.gy/216.239.38.120 +address=/google.gy/216.239.38.120 +address=/www.google.hn/216.239.38.120 +address=/google.hn/216.239.38.120 +address=/www.google.hr/216.239.38.120 +address=/google.hr/216.239.38.120 +address=/www.google.ht/216.239.38.120 +address=/google.ht/216.239.38.120 +address=/www.google.hu/216.239.38.120 +address=/google.hu/216.239.38.120 +address=/www.google.ie/216.239.38.120 +address=/google.ie/216.239.38.120 +address=/www.google.im/216.239.38.120 +address=/google.im/216.239.38.120 +address=/www.google.iq/216.239.38.120 +address=/google.iq/216.239.38.120 +address=/www.google.is/216.239.38.120 +address=/google.is/216.239.38.120 +address=/www.google.it/216.239.38.120 +address=/google.it/216.239.38.120 +address=/www.google.je/216.239.38.120 +address=/google.je/216.239.38.120 +address=/www.google.jo/216.239.38.120 +address=/google.jo/216.239.38.120 +address=/www.google.ki/216.239.38.120 +address=/google.ki/216.239.38.120 +address=/www.google.kg/216.239.38.120 +address=/google.kg/216.239.38.120 +address=/www.google.kz/216.239.38.120 +address=/google.kz/216.239.38.120 +address=/www.google.la/216.239.38.120 +address=/google.la/216.239.38.120 +address=/www.google.li/216.239.38.120 +address=/google.li/216.239.38.120 +address=/www.google.lk/216.239.38.120 +address=/google.lk/216.239.38.120 +address=/www.google.lt/216.239.38.120 +address=/google.lt/216.239.38.120 +address=/www.google.lu/216.239.38.120 +address=/google.lu/216.239.38.120 +address=/www.google.lv/216.239.38.120 +address=/google.lv/216.239.38.120 +address=/www.google.md/216.239.38.120 +address=/google.md/216.239.38.120 +address=/www.google.me/216.239.38.120 +address=/google.me/216.239.38.120 +address=/www.google.mg/216.239.38.120 +address=/google.mg/216.239.38.120 +address=/www.google.mk/216.239.38.120 +address=/google.mk/216.239.38.120 +address=/www.google.ml/216.239.38.120 +address=/google.ml/216.239.38.120 +address=/www.google.mn/216.239.38.120 +address=/google.mn/216.239.38.120 +address=/www.google.ms/216.239.38.120 +address=/google.ms/216.239.38.120 +address=/www.google.mu/216.239.38.120 +address=/google.mu/216.239.38.120 +address=/www.google.mv/216.239.38.120 +address=/google.mv/216.239.38.120 +address=/www.google.mw/216.239.38.120 +address=/google.mw/216.239.38.120 +address=/www.google.ne/216.239.38.120 +address=/google.ne/216.239.38.120 +address=/www.google.nl/216.239.38.120 +address=/google.nl/216.239.38.120 +address=/www.google.no/216.239.38.120 +address=/google.no/216.239.38.120 +address=/www.google.nr/216.239.38.120 +address=/google.nr/216.239.38.120 +address=/www.google.nu/216.239.38.120 +address=/google.nu/216.239.38.120 +address=/www.google.pl/216.239.38.120 +address=/google.pl/216.239.38.120 +address=/www.google.pn/216.239.38.120 +address=/google.pn/216.239.38.120 +address=/www.google.ps/216.239.38.120 +address=/google.ps/216.239.38.120 +address=/www.google.pt/216.239.38.120 +address=/google.pt/216.239.38.120 +address=/www.google.ro/216.239.38.120 +address=/google.ro/216.239.38.120 +address=/www.google.ru/216.239.38.120 +address=/google.ru/216.239.38.120 +address=/www.google.rw/216.239.38.120 +address=/google.rw/216.239.38.120 +address=/www.google.sc/216.239.38.120 +address=/google.sc/216.239.38.120 +address=/www.google.se/216.239.38.120 +address=/google.se/216.239.38.120 +address=/www.google.sh/216.239.38.120 +address=/google.sh/216.239.38.120 +address=/www.google.si/216.239.38.120 +address=/google.si/216.239.38.120 +address=/www.google.sk/216.239.38.120 +address=/google.sk/216.239.38.120 +address=/www.google.sn/216.239.38.120 +address=/google.sn/216.239.38.120 +address=/www.google.so/216.239.38.120 +address=/google.so/216.239.38.120 +address=/www.google.sm/216.239.38.120 +address=/google.sm/216.239.38.120 +address=/www.google.sr/216.239.38.120 +address=/google.sr/216.239.38.120 +address=/www.google.st/216.239.38.120 +address=/google.st/216.239.38.120 +address=/www.google.td/216.239.38.120 +address=/google.td/216.239.38.120 +address=/www.google.tg/216.239.38.120 +address=/google.tg/216.239.38.120 +address=/www.google.tl/216.239.38.120 +address=/google.tl/216.239.38.120 +address=/www.google.tm/216.239.38.120 +address=/google.tm/216.239.38.120 +address=/www.google.tn/216.239.38.120 +address=/google.tn/216.239.38.120 +address=/www.google.to/216.239.38.120 +address=/google.to/216.239.38.120 +address=/www.google.tt/216.239.38.120 +address=/google.tt/216.239.38.120 +address=/www.google.vg/216.239.38.120 +address=/google.vg/216.239.38.120 +address=/www.google.vu/216.239.38.120 +address=/google.vu/216.239.38.120 +address=/www.google.ws/216.239.38.120 +address=/google.ws/216.239.38.120 +address=/www.google.rs/216.239.38.120 +address=/google.rs/216.239.38.120 +address=/www.google.cat/216.239.38.120 +address=/google.cat/216.239.38.120 +address=/ya.ru/213.180.193.56 +address=/yandex.ru/213.180.193.56 +address=/yandex.com/213.180.193.56 +address=/yandex.com.tr/213.180.193.56 +address=/yandex.ua/213.180.193.56 +address=/yandex.by/213.180.193.56 +address=/yandex.ee/213.180.193.56 +address=/yandex.lt/213.180.193.56 +address=/yandex.lv/213.180.193.56 +address=/yandex.md/213.180.193.56 +address=/yandex.uz/213.180.193.56 +address=/yandex.tm/213.180.193.56 +address=/yandex.tj/213.180.193.56 +address=/yandex.az/213.180.193.56 +address=/www.youtube.com/216.239.38.119 +address=/m.youtube.com/216.239.38.119 +address=/youtubei.googleapis.com/216.239.38.119 +address=/youtube.googleapis.com/216.239.38.119 +address=/www.youtube-nocookie.com/216.239.38.119 +EOF + fi +} + +gen(){ + echo '#!/bin/sh' > $FWI +} + +add_rule(){ + ipset -N blockip hash:net 2>/dev/null + for i in $(cat $L/ip.list);do ipset -! add blockip $i;done + iptables -I FORWARD -m set --match-set blockip dst -m comment --comment "$TAG" -j DROP + iptables -I OUTPUT -m set --match-set blockip dst -m comment --comment "$TAG" -j DROP + gen + extract_rules(){ + echo "*$1" + iptables-save -t $1 | grep DNSFILTER |\ + sed -e "s/^-A \(OUTPUT\|FORWARD\)/-I \1 1/" + echo 'COMMIT' + } + cat <<-EOF >> $FWI + iptables-save -c | grep -v DNSFILTER | iptables-restore -c + iptables-restore -n <<-EOT + $(extract_rules filter) + EOT +EOF +} + +add_cron(){ + if [ $cron_mode = 1 ];then + if ! grep -wq "$time_update \* \* \* .*dnsfilter" $CRON_FILE;then + grep -q dnsfilter $CRON_FILE && sed -i '/dnsfilter/d' $CRON_FILE + echo "0 $time_update * * * $P/dnsfilter > /tmp/adupdate.log 2>&1" >> $CRON_FILE + /etc/init.d/cron restart + fi + else + del_cron + fi +} + +del_cron(){ + if grep -q dnsfilter $CRON_FILE;then + sed -i '/dnsfilter/d' $CRON_FILE + /etc/init.d/cron restart + fi +} + +del_rule(){ + iptables -D FORWARD -m set --match-set blockip dst -m comment --comment "$TAG" -j DROP 2>/dev/null + iptables -D OUTPUT -m set --match-set blockip dst -m comment --comment "$TAG" -j DROP 2>/dev/null + ipset -X blockip 2>/dev/null + gen +} + +start(){ + config_load dnsfilter + config_foreach get_config dnsfilter + if [ $enable = 0 ];then + echo "`eval $E` [DNSFilter is disabled]" + exit 1 + fi + if [ -s $D/rules.conf ];then + echo "`eval $E` [DNSFilter is running]" + exit 1 + fi + if [ -n "$url" ];then + [ $flash = 0 -a ! -s $T/rules.conf ] && B=1 + [ $flash = 1 -a ! -s $L/rules/rules.conf ] && B=1 + fi + if [ $B = 1 ];then + echo "`eval $E` [Download Subscribe Rules...]" + $P/addown --down $B >/dev/null 2>&1 & + exit 9 + fi + echo "`eval $E` [Load DNSFilter Rules]" + add_dns + add_rule + add_cron + if [ $STATUS = Y ];then + echo "`eval $E` [Dnsmasq Change]" + /etc/init.d/dnsmasq restart >/dev/null 2>&1 + fi +} + +stop(){ + del_rule + kill -9 $(ps -w | grep grep $P/dnsfilter | grep -v grep | awk '{print$1}') 2>/dev/null + kill -9 $(ps -w | grep grep $P/addown | grep -v grep | awk '{print$1}') 2>/dev/null + kill -9 $(ps -w | grep ad_new.conf | grep -v grep | awk '{print$1}') 2>/dev/null + echo "`eval $E` [Stop DNSFilter]" + rm -rf $DNSMASQ_CONF_DIR/dnsfilter.conf $D /var/lock/dnsfilter.lock + if [ "$(echo $url | sed 's/ /\n/g' | sort -u)" != "$(cat $T/url 2>/dev/null)" ];then + rm -rf $T + [ -d $L/rules ] && rm -rf $L/rules + fi + ([ -h $T/url -a $flash = 0 ] || [ -z "$url" ]) && rm -rf $T + [ $enable = 0 ] && del_cron + if [ $STATUS = Y ];then + rm -rf $T + echo "`eval $E` [Revert Dnsmasq]" + /etc/init.d/dnsmasq restart >/dev/null 2>&1 + rm -f /tmp/adupdate.log + fi +} + +restart(){ + if [ $enable = 1 ];then + STATUS=N + stop + start + echo "`eval $E` [Restart Dnsmasq]" + /etc/init.d/dnsmasq restart >/dev/null 2>&1 + else + stop + fi +} + +boot(){ + gen;start +} diff --git a/luci-app-dnsfilter/root/etc/uci-defaults/luci-dnsfilter b/luci-app-dnsfilter/root/etc/uci-defaults/luci-dnsfilter new file mode 100755 index 000000000..342126b18 --- /dev/null +++ b/luci-app-dnsfilter/root/etc/uci-defaults/luci-dnsfilter @@ -0,0 +1,17 @@ +#!/bin/sh +uci -q batch <<-EOF >/dev/null + delete ucitrack.@dnsfilter[-1] + add ucitrack dnsfilter + set ucitrack.@dnsfilter[-1].init=dnsfilter + commit ucitrack + delete firewall.dnsfilter + set firewall.dnsfilter=include + set firewall.dnsfilter.type=script + set firewall.dnsfilter.path=/var/etc/dnsfilter.include + set firewall.dnsfilter.reload=1 + commit firewall +EOF + +chmod 755 /etc/init.d/dnsfilter /usr/share/dnsfilter/* >/dev/null 2>&1 +rm -rf /tmp/luci-* +exit 0 diff --git a/luci-app-dnsfilter/root/usr/share/dnsfilter/addown b/luci-app-dnsfilter/root/usr/share/dnsfilter/addown new file mode 100755 index 000000000..4c70b2558 --- /dev/null +++ b/luci-app-dnsfilter/root/usr/share/dnsfilter/addown @@ -0,0 +1,21 @@ +#!/bin/sh +[ "$1" = --down ] || exit 1 +# 防止重复启动 +LOCK=/var/lock/dnsfilter.lock +[ -f $LOCK ] && exit 1 +touch $LOCK + +B=/tmp/dnsfilter +C=/tmp/adupdate.log +D="date +'%Y-%m-%d %H:%M:%S'" +E="curl -kLfso" +G="Download Subscribe Rules" + +if [ "$2" = 1 ];then + echo "`eval $D` [$G]" >> $C + /usr/share/dnsfilter/dnsfilter addown >> $C +fi + +echo "`eval $D` [Start DNSFilter]" >> $C;echo `eval $D` > $B/dnsfilter.updated +rm -f $LOCK +/etc/init.d/dnsfilter start & diff --git a/luci-app-dnsfilter/root/usr/share/dnsfilter/dnsfilter b/luci-app-dnsfilter/root/usr/share/dnsfilter/dnsfilter new file mode 100755 index 000000000..e2cb22c5a --- /dev/null +++ b/luci-app-dnsfilter/root/usr/share/dnsfilter/dnsfilter @@ -0,0 +1,103 @@ +#!/bin/sh +# 防止重复启动 +LOCK=/var/lock/dnsfilter.lock +if [ -f $LOCK ];then + case $1 in + gen|addown)X=1;; + *)exit 1;; + esac +fi +touch $LOCK + +B="Download Subscribe Rules" +C=0 +D=0 +E="date +'%Y-%m-%d %H:%M:%S'" +U=`uci -q get dnsfilter.@dnsfilter[0].url` +P=/tmp/dnsfilter +W=`cat /etc/dnsfilter/white.list` + +gen(){ + cat /tmp/adnew.conf | grep ^\|\|[^\*]*\^$ | grep -Ev "^\|\|[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}*" | sed -e 's:||:address=/:' -e 's:\^:/:' > /tmp/ad.conf + for i in $W;do sed -i -e "/\/$i\//d" -e "/\.$i\//d" /tmp/ad.conf;done + rm -f /tmp/adnew.conf +} + +down(){ + G=/tmp/ad_tmp + F=$G/ad_new.conf + rm -rf $G + mkdir -p $G $P + for i in $U;do + X=1 + while ! curl --connect-timeout 6 --retry 2 -m 60 -kLfso $F $i;do + [ $X -ge 20 ] && echo "`eval $E` [Download $i Failed]" && continue 2 || let X++ + sleep 2 + done + X=`md5sum $G/rules.conf 2>/dev/null | awk '{print$1}'` + Y=`md5sum $G/host 2>/dev/null | awk '{print$1}'` + sed -i -e '/127.0.0.1 #/d' -e '/127.0.0.1 !/d' -e 's:#.*::' -e 's:!.*::' -e 's/\$important//g' -e 's/[ \t]*$//g' -e 's/^[ \t]*//g' -e '/\*/d' -e '/^$/d' $F + sed -i "s/\r//g" $F + if grep -q "^address=" $F;then + cat $F >> $G/rules.conf + elif grep -q -e "^0.0.0.0 " -e "^127.0.0.1 " $F;then + cat $F >> $G/host + elif ! grep -q -e "|" -e "@" $F;then + cat $F | sed -e 's:^:address=/:' -e 's:$:/:' >> $G/rules.conf + else + cat $F | grep ^\|\|[^\*]*\^$ | grep -Ev "^\|\|[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}*" | sed -e 's:||:address=/:' -e 's:\^.*:/:' >> $G/rules.conf + fi + [ "$X" = "`md5sum $G/rules.conf 2>/dev/null | awk '{print$1}'`" -a "$Y" = "`md5sum $G/host 2>/dev/null | awk '{print$1}'`" ] && echo "`eval $E` [Conversion $i Failed]" + echo $i >> $G/url + done + [ -s $G/host ] && sed -e '/:/d' -e '/ 0.0.0.0/d' -e '/255.255.255.255/d' -e '/ local/d' -e 's:127.0.0.1 :address=/:' -e 's:0.0.0.0 :address=/:' -e 's:$:/:' $G/host >> $G/rules.conf + [ -s $G/rules.conf ] && sed -i -e 's:/127.0.0.1$:/:' -e 's:/0.0.0.0$:/:' $G/rules.conf && echo "`sort -u $G/rules.conf`" > $G/rules.conf + [ -s $G/url ] && echo "`sort -u $G/url`" > $G/url + if [ -s $G/rules.conf ];then + echo "`eval $E` [$B Successful]" + rm -f $F $G/host $P/failed + for i in $W;do sed -i -e "/\/$i\//d" -e "/\.$i\//d" $G/rules.conf;done + [ "$1" = 2 ] && rm -f $LOCK && exit + X=`uci -q get dnsfilter.@dnsfilter[0].flash` + Y=`md5sum $G/* | awk '{print$1}'` + [ $X = 0 ] && Z=`md5sum $P/* 2>/dev/null | awk '{print$1}'` || Z=`md5sum /etc/dnsfilter/rules/* 2>/dev/null | awk '{print$1}'` + if [ "$Y" != "$Z" ];then + [ "$1" = 1 ] || echo "`eval $E` [Subscribe Rules Need Update]" + if [ "$X" = 0 ];then + rm -f $P/* + cp -a $G/* $P + else + [ ! -d "/etc/dnsfilter/rules" ] && + mkdir /etc/dnsfilter/rules + rm -f /etc/dnsfilter/rules/* + cp -a $G/* /etc/dnsfilter/rules + fi + D=1 + else + echo "`eval $E` [Subscribe Rules No Change]" + fi + else + echo "`eval $E` [$B Failed]" + echo failed > $P/failed + [ "$1" = 2 ] && rm -f $LOCK && exit + fi + rm -rf $G +} + +case $1 in + addown)down 1;exit;; + down)down 2;; + gen)gen;[ "$X" = 1 ] || rm -f $LOCK;exit;; +esac + +if [ `uci -q get dnsfilter.@dnsfilter[0].enable` = 1 ];then + [ -n "$U" ] && down + echo `eval $E` > $P/dnsfilter.updated +fi + +if [ $D = 1 ];then + echo "`eval $E` [Reload DNSFilter Rules]" + /etc/init.d/dnsfilter restart +fi + +rm -f $LOCK diff --git a/luci-app-dnsfilter/root/usr/share/rpcd/acl.d/luci-app-dnsfilter.json b/luci-app-dnsfilter/root/usr/share/rpcd/acl.d/luci-app-dnsfilter.json new file mode 100644 index 000000000..05c8677a3 --- /dev/null +++ b/luci-app-dnsfilter/root/usr/share/rpcd/acl.d/luci-app-dnsfilter.json @@ -0,0 +1,24 @@ +{ + "luci-app-dnsfilter": { + "description": "Grant UCI access for luci-app-dnsfilter", + "read": { + "file": { + "/etc/init.d/dnsfilter": [ "exec" ], + "/usr/share/dnsfilter/addown": [ "exec" ], + "/usr/share/dnsfilter/dnsfilter": [ "exec" ], + "/tmp/dnsfilter/rules.conf": [ "read" ] + }, + "uci": [ "dnsfilter" ] + }, + "write": { + "file": { + "/etc/dnsfilter/black.list": [ "write" ], + "/etc/dnsfilter/ip.list": [ "write" ], + "/etc/dnsfilter/white.list": [ "write" ], + "/etc/dnsfilter/rules/rules.conf": [ "write" ], + "/etc/dnsfilter/url": [ "write" ] + }, + "uci": [ "dnsfilter" ] + } + } +} diff --git a/luci-app-fullconenat/Makefile b/luci-app-fullconenat/Makefile new file mode 100755 index 000000000..0fbbfaa66 --- /dev/null +++ b/luci-app-fullconenat/Makefile @@ -0,0 +1,14 @@ +#-- Copyright (C) 2018 dz + +include $(TOPDIR)/rules.mk + +LUCI_TITLE:=LuCI support for FullConeNat +LUCI_DEPENDS:=+iptables-mod-fullconenat +LUCI_PKGARCH:=all +PKG_VERSION:=1.3 +PKG_RELEASE:=3 + +include $(TOPDIR)/feeds/luci/luci.mk + +# call BuildPackage - OpenWrt buildroot signature + diff --git a/luci-app-fullconenat/README.md b/luci-app-fullconenat/README.md new file mode 100644 index 000000000..b260a82b8 --- /dev/null +++ b/luci-app-fullconenat/README.md @@ -0,0 +1,5 @@ +# luci-app-fullconenat + +本软件包是 [fullconenat]的 LuCI 控制界面 + +[fullconenat]: https://github.com/LGA1150/openwrt-fullconenat diff --git a/luci-app-fullconenat/luasrc/controller/fullconenat.lua b/luci-app-fullconenat/luasrc/controller/fullconenat.lua new file mode 100755 index 000000000..5325ef4ce --- /dev/null +++ b/luci-app-fullconenat/luasrc/controller/fullconenat.lua @@ -0,0 +1,12 @@ +module("luci.controller.fullconenat", package.seeall) + +function index() + if not nixio.fs.access("/etc/config/fullconenat") then + return + end + local page + page = entry({"admin", "network", "fullconenat"}, cbi("fullconenat"), _("fullconenat"), 101) + page.i18n = "fullconenat" + page.dependent = true + page.acl_depends = { "luci-app-fullconenat" } +end diff --git a/luci-app-fullconenat/luasrc/model/cbi/fullconenat.lua b/luci-app-fullconenat/luasrc/model/cbi/fullconenat.lua new file mode 100755 index 000000000..f68eed518 --- /dev/null +++ b/luci-app-fullconenat/luasrc/model/cbi/fullconenat.lua @@ -0,0 +1,65 @@ +#-- Copyright (C) 2018 dz + +local fwm = require "luci.model.firewall".init() +local def = fwm:get_defaults() +local zn = fwm:get_zone("wan") +local m, s, o, fw3_buildin, has_module, status, des + +local function testcmd (cmd) + return luci.sys.call(cmd) == 0 +end + +has_module = testcmd("modprobe -q xt_FULLCONENAT") +fw3_buildin = testcmd("strings `which fw3` | grep -q fullcone") + +m = Map("fullconenat", translate("Full cone NAT"), + translate("FullConeNat.")) +status="Not supported, Kernel module needed: xt_FULLCONENAT" +if has_module then +if testcmd("iptables -t nat -L -n --line-numbers | grep FULLCONENAT >/dev/null") then + status="Running" +else + status="Not Running" +end +end + +m = Map("fullconenat", translate("FullConeNat"), "%s - %s" %{translate("FULLCONENAT"), translate(status)}) + +des = fw3_buildin and "Build-in mode, set the `fullcone` option to firewall configure either." or "Manual mode, write to the firewall custom rules settings only." +s = m:section(TypedSection, "fullconenat", translate("Settings"), translate(des)) +s.anonymous = true + +o = s:option(ListValue, "mode", translate("Register modes"), translate("Warning!!! There is security risk if enabled.")) +o.widget = "radio" +o.orientation = "horizontal" +o.default = "disable" +o.rmempty = false +o:value("disable", translate("Disable")) +o:value("ips", translate("IP Address Only")) +o:value("all", translate("ALL Enabled")) +o.cfgvalue = function (self, sec) + local ret = "disable" + if fw3_buildin and def:get("fullcone") == "1" then + ret = "all" + else + ret = self.map:get(sec, self.option) + end + return has_module and ret or "disable" +end +o.write = function (self, sec, val) + val = has_module and val or "disable" + if fw3_buildin then + def:set("fullcone", val == "all" and 1 or 0) + zn:set("fullcone", val == "all" and 1 or 0) + end + fwm.commit() + return self.map:set(sec, self.option, val) +end + +o = s:option(Value, "fullconenat_ip", translate("FullConeNat IP"), translate("Enable FullConeNat for specified IP Address.") .. "
" .. (fw3_buildin and translate("Manual mode, write to the firewall custom rules settings only.") or "")) +o.placeholder="192.168.1.100,192.168.1.101,192.168.1.102" +o.rempty = true +o.optional = false +o:depends("mode", "ips") + +return m diff --git a/luci-app-fullconenat/po/zh-cn/fullconenat.po b/luci-app-fullconenat/po/zh-cn/fullconenat.po new file mode 100755 index 000000000..e629c3405 --- /dev/null +++ b/luci-app-fullconenat/po/zh-cn/fullconenat.po @@ -0,0 +1,45 @@ +msgid "fullconenat" +msgstr "全端口映射" + +msgid "FULLCONENAT" +msgstr "Fullcone NAT" + +msgid "Running" +msgstr "正在运行" + +msgid "Not Running" +msgstr "未运行" + +msgid "Not supported, Kernel module needed: xt_FULLCONENAT" +msgstr "不支持,缺少 xt_FULLCONENAT 内核组件" + +msgid "Settings" +msgstr "设置" + +msgid "Build-in mode, set the `fullcone` option to firewall configure either." +msgstr "防火墙内置模式,同时配置防火墙(firewall)中对应的选项。" + +msgid "Manual mode, write to the firewall custom rules settings only." +msgstr "手动模式,仅将配置写入到防火墙(firewall)自定义规则中。" + +msgid "Register modes" +msgstr "运行模式" + +msgid "Disable" +msgstr "停用" + +msgid "IP Address Only" +msgstr "限指定IP" + +msgid "ALL Enabled" +msgstr "全网开启" + +msgid "Warning!!! There is security risk if enabled." +msgstr "警告!!!开启后存在安全风险。" + +msgid "FullConeNat IP" +msgstr "映射IP" + +msgid "Enable FullConeNat for specified IP Address." +msgstr "多IP映射用英文逗号分隔。" + diff --git a/luci-app-fullconenat/po/zh_Hans b/luci-app-fullconenat/po/zh_Hans new file mode 120000 index 000000000..41451e4a1 --- /dev/null +++ b/luci-app-fullconenat/po/zh_Hans @@ -0,0 +1 @@ +zh-cn \ No newline at end of file diff --git a/luci-app-fullconenat/root/etc/config/fullconenat b/luci-app-fullconenat/root/etc/config/fullconenat new file mode 100755 index 000000000..919a8ad8c --- /dev/null +++ b/luci-app-fullconenat/root/etc/config/fullconenat @@ -0,0 +1,3 @@ +config fullconenat 'config' + option mode 'disable' + option fullconenat_ip '192.168.1.100' diff --git a/luci-app-fullconenat/root/etc/init.d/fullconenat b/luci-app-fullconenat/root/etc/init.d/fullconenat new file mode 100755 index 000000000..4c843c5bc --- /dev/null +++ b/luci-app-fullconenat/root/etc/init.d/fullconenat @@ -0,0 +1,70 @@ +#!/bin/sh /etc/rc.common +#-- Copyright (C) 2018 dz + +START=99 + +re=0 + +start(){ + local fw3_buildin mode fullconenat_ip fullcone masq + strings `which fw3` | grep -q "fullcone" + fw3_buildin=$? + mode=$(uci get fullconenat.config.mode 2>/dev/null) + if modprobe -q "xt_FULLCONENAT"; then + [ $fw3_buildin -eq 0 ] && echo -n "fw3 build-in, change settings in /etc/config/firewall either. " + echo "$mode." + else + echo "not supported." + return 1 + fi + fullcone=0 + fullconenat_ip=$(uci get fullconenat.config.fullconenat_ip 2>/dev/null) + if [ "$mode" == "ips" ]; then + sed -i '/FULLCONENAT/d' /etc/firewall.user + echo "iptables -t nat -A zone_wan_prerouting -j FULLCONENAT" >> /etc/firewall.user + echo "iptables -t nat -A zone_wan_postrouting -s $fullconenat_ip -j FULLCONENAT" >> /etc/firewall.user + echo "iptables -t nat -A zone_wan_postrouting -j MASQUERADE" >> /etc/firewall.user + elif [ "$mode" == "all" ]; then + if [ $fw3_buildin -ne 0 ]; then + iptables -t nat -D zone_wan_postrouting -s $fullconenat_ip -j FULLCONENAT + iptables -t nat -D zone_wan_postrouting -j MASQUERADE + sed -i '/zone_wan_postrouting -j MASQUERADE/d' /etc/firewall.user + sed -i '/FULLCONENAT/d' /etc/firewall.user + echo "iptables -t nat -A zone_wan_prerouting -j FULLCONENAT" >> /etc/firewall.user + echo "iptables -t nat -A zone_wan_postrouting -j FULLCONENAT" >> /etc/firewall.user + else + fullcone=1 + fi + fi + [ $fw3_buildin -eq 0 ] && { + uci set firewall.@defaults[0].fullcone=$fullcone + uci set firewall.@zone[1].fullcone=$fullcone + } + uci commit firewall + /etc/init.d/firewall restart +} + +stop(){ + fullconenat_ip=$(uci get fullconenat.config.fullconenat_ip 2>/dev/null) + mode=$(uci get fullconenat.config.mode 2>/dev/null) + echo "$mode, $fullconenat_ip" + iptables -t nat -D zone_wan_prerouting -j FULLCONENAT + iptables -t nat -D zone_wan_postrouting -s $fullconenat_ip -j FULLCONENAT + iptables -t nat -D zone_wan_postrouting -j MASQUERADE + iptables -t nat -D zone_wan_postrouting -j FULLCONENAT + sed -i '/zone_wan_postrouting -j MASQUERADE/d' /etc/firewall.user + sed -i '/FULLCONENAT/d' /etc/firewall.user + [ $re -eq 0 ] && { + uci set firewall.@defaults[0].fullcone=0 + uci set firewall.@zone[1].fullcone=0 + uci commit firewall + /etc/init.d/firewall restart + } +} + + +restart(){ + re=1 + stop + start +} diff --git a/luci-app-fullconenat/root/etc/uci-defaults/fullconenat b/luci-app-fullconenat/root/etc/uci-defaults/fullconenat new file mode 100755 index 000000000..58ced1f02 --- /dev/null +++ b/luci-app-fullconenat/root/etc/uci-defaults/fullconenat @@ -0,0 +1,13 @@ +#!/bin/sh + +uci -q batch <<-EOF >/dev/null + delete ucitrack.@fullconenat[-1] + add ucitrack fullconenat + set ucitrack.@fullconenat[-1].init=fullconenat + commit ucitrack +EOF + +/etc/init.d/fullconenat enable + +rm -f /tmp/luci-indexcache +exit 0 diff --git a/luci-app-fullconenat/root/usr/share/rpcd/acl.d/luci-app-fullconenat.json b/luci-app-fullconenat/root/usr/share/rpcd/acl.d/luci-app-fullconenat.json new file mode 100644 index 000000000..2a1c373e7 --- /dev/null +++ b/luci-app-fullconenat/root/usr/share/rpcd/acl.d/luci-app-fullconenat.json @@ -0,0 +1,11 @@ +{ + "luci-app-fullconenat": { + "description": "Grant UCI access for luci-app-fullconenat", + "read": { + "uci": [ "fullconenat" ] + }, + "write": { + "uci": [ "fullconenat" ] + } + } +} diff --git a/luci-app-iperf3-server/Makefile b/luci-app-iperf3-server/Makefile new file mode 100755 index 000000000..ad850e14d --- /dev/null +++ b/luci-app-iperf3-server/Makefile @@ -0,0 +1,13 @@ +# Copyright (C) 2020-2023 Hyy2001X + +include $(TOPDIR)/rules.mk + +LUCI_TITLE:=LuCI support for iPerf3 server +LUCI_DEPENDS:=+iperf3 +LUCI_PKGARCH:=all +PKG_VERSION:=2.0 +PKG_RELEASE:=4 + +include $(TOPDIR)/feeds/luci/luci.mk + +# call BuildPackage - OpenWrt buildroot signature diff --git a/luci-app-iperf3-server/README.md b/luci-app-iperf3-server/README.md new file mode 100755 index 000000000..6cb710c0e --- /dev/null +++ b/luci-app-iperf3-server/README.md @@ -0,0 +1,3 @@ +# luci-app-iperf3-server + +一个简单的 iPerf3 服务端 diff --git a/luci-app-iperf3-server/luasrc/controller/iperf3-server.lua b/luci-app-iperf3-server/luasrc/controller/iperf3-server.lua new file mode 100755 index 000000000..02cef90c4 --- /dev/null +++ b/luci-app-iperf3-server/luasrc/controller/iperf3-server.lua @@ -0,0 +1,17 @@ +module("luci.controller.iperf3-server",package.seeall) + +function index() + if not nixio.fs.access("/etc/config/iperf3-server") then + return + end + + entry({"admin", "services", "iperf3-server"}, cbi("iperf3-server"), _("iPerf3 Server"),99) + entry({"admin", "services", "iperf3-server", "status"}, call("act_status")).leaf = true +end + +function act_status() + local e = {} + e.running = luci.sys.call("pgrep iperf3 > /dev/null") == 0 + luci.http.prepare_content("application/json") + luci.http.write_json(e) +end diff --git a/luci-app-iperf3-server/luasrc/model/cbi/iperf3-server.lua b/luci-app-iperf3-server/luasrc/model/cbi/iperf3-server.lua new file mode 100755 index 000000000..e8417dc34 --- /dev/null +++ b/luci-app-iperf3-server/luasrc/model/cbi/iperf3-server.lua @@ -0,0 +1,36 @@ +m = Map("iperf3-server", translate("iPerf3 Server"), translate("iPerf3 - The ultimate speed test tool for TCP, UDP and SCTP")) + +m:section(SimpleSection).template = "iperf3-server/iperf3-server_status" + +s = m:section(TypedSection, "iperf3-server", "") +s.addremove = false +s.anonymous = true + +main_enable = s:option(Flag, "main_enable", translate("Enable"), translate("Enable iPerf3 Servers")) +main_enable.default = "0" +main_enable.rmempty = false + +s = m:section(TypedSection, "servers", translate("Server Settings"), translate("Set up Multi-iPerf3 Servers")) +s.anonymous = true +s.addremove = true +s.template = "cbi/tblsection" + +enable_server = s:option(Flag, "enable_server", translate("Enable")) +enable_server.default = "1" +enable_server.rmempty = false + +port = s:option(Value, "port", translate("Port")) +port.datatype = "port" +port.default = "5201" +port.rmempty = false + +delay = s:option(Value, "delay", translate("Start delay (Seconds)")) +delay.default = "0" +delay.datatype = "uinteger" +delay.rmempty = false + +extra_options = s:option(Value, "extra_options", translate("Extra Options")) +extra_options.rmempty = true +extra_options.password= false + +return m diff --git a/luci-app-iperf3-server/luasrc/view/iperf3-server/iperf3-server_status.htm b/luci-app-iperf3-server/luasrc/view/iperf3-server/iperf3-server_status.htm new file mode 100755 index 000000000..c6a8378a2 --- /dev/null +++ b/luci-app-iperf3-server/luasrc/view/iperf3-server/iperf3-server_status.htm @@ -0,0 +1,22 @@ + + +
+

+ <%:Collecting data...%> +

+
diff --git a/luci-app-iperf3-server/po/zh-cn/iperf3-server.po b/luci-app-iperf3-server/po/zh-cn/iperf3-server.po new file mode 100755 index 000000000..bfbd1f951 --- /dev/null +++ b/luci-app-iperf3-server/po/zh-cn/iperf3-server.po @@ -0,0 +1,26 @@ +msgid "iPerf3 - The ultimate speed test tool for TCP, UDP and SCTP" +msgstr "iPerf3 是一款集 TCP UDP 和 SCTP 于的一身的终极速度测试工具" + +msgid "Port" +msgstr "端口" + +msgid "iPerf3 Server" +msgstr "iPerf3 服务器" + +msgid "Enable iPerf3 Servers" +msgstr "启用 iPerf3 服务器" + +msgid "Server Settings" +msgstr "服务端设置" + +msgid "Set up Multi-iPerf3 Servers" +msgstr "设置多个 iPerf3 服务端" + +msgid "Start delay" +msgstr "启动延迟 (秒)" + +msgid "iPerf3 Server listening port" +msgstr "iPerf3 服务端监听端口" + +msgid "Extra Options" +msgstr "额外参数" diff --git a/luci-app-iperf3-server/po/zh_Hans/iperf3-server.po b/luci-app-iperf3-server/po/zh_Hans/iperf3-server.po new file mode 100755 index 000000000..bfbd1f951 --- /dev/null +++ b/luci-app-iperf3-server/po/zh_Hans/iperf3-server.po @@ -0,0 +1,26 @@ +msgid "iPerf3 - The ultimate speed test tool for TCP, UDP and SCTP" +msgstr "iPerf3 是一款集 TCP UDP 和 SCTP 于的一身的终极速度测试工具" + +msgid "Port" +msgstr "端口" + +msgid "iPerf3 Server" +msgstr "iPerf3 服务器" + +msgid "Enable iPerf3 Servers" +msgstr "启用 iPerf3 服务器" + +msgid "Server Settings" +msgstr "服务端设置" + +msgid "Set up Multi-iPerf3 Servers" +msgstr "设置多个 iPerf3 服务端" + +msgid "Start delay" +msgstr "启动延迟 (秒)" + +msgid "iPerf3 Server listening port" +msgstr "iPerf3 服务端监听端口" + +msgid "Extra Options" +msgstr "额外参数" diff --git a/luci-app-iperf3-server/root/etc/config/iperf3-server b/luci-app-iperf3-server/root/etc/config/iperf3-server new file mode 100755 index 000000000..5a752470c --- /dev/null +++ b/luci-app-iperf3-server/root/etc/config/iperf3-server @@ -0,0 +1,4 @@ + +config iperf3-server + option enable '0' + option port '5201' \ No newline at end of file diff --git a/luci-app-iperf3-server/root/etc/init.d/iperf3-server b/luci-app-iperf3-server/root/etc/init.d/iperf3-server new file mode 100755 index 000000000..81252f182 --- /dev/null +++ b/luci-app-iperf3-server/root/etc/init.d/iperf3-server @@ -0,0 +1,55 @@ +#!/bin/sh /etc/rc.common + +START=99 +LOGGER="logger -t [iPerf3-Server]" + +start() { + local basic_list="main_enable" + local server_list="port delay extra_options enable_server" + for i in $(echo $basic_list) + do + local eval $i="$(uci_get_by_type iperf3-server 0 $i)" + done ; unset i + if [ "$main_enable" == 1 ] + then + server_number=$(uci show iperf3-server 2> /dev/null | egrep '@servers\[[0-9]\]+=servers' | wc -l) + # server_number=$(uci show iperf3-server 2> /dev/null | egrep -o '@servers\[[0-9]\]+=servers' | awk 'END {print}' | egrep -o "[0-9]") + for u in $(seq 0 $((${server_number} - 1))) + do + for i in $server_list + do + eval ${i}=$(uci_get_by_type servers $u $i) + done ; unset i + if [ "$enable_server" == 1 ] + then + sleep $delay + $LOGGER "Starting iPerf3 Server [$u] with Port [$port] ..." + old_process="$(ps -efww | grep 'iperf3 -s -D -p $port' | grep -v 'grep' | awk '{print $1}')" + [ "$old_process" ] && kill -9 "$old_process" 2> /dev/null + $(command -v iperf3) -s -D -p $port $extra_options + #procd_open_instance + #procd_set_param command sleep $delay ; $(command -v iperf3) -s -D -p $port $extra_options + #procd_set_param respawn 3000 3 10 + #procd_close_instance + fi + unset enable_server delay + done ; unset u + else + $LOGGER "iPerf3 Server is disabled ..." + stop_service + fi +} + +stop() { + $LOGGER "Stopping iPerf3 Server ..." + ps -efww | grep 'iperf3 -s -D' | grep -v 'grep' | awk '{print $1}' | xargs kill -9 +} + +service_triggers() { + procd_add_reload_trigger "iperf3-server" +} + +uci_get_by_type() { + local ret=$(uci get iperf3-server.@$1[$2].$3 2>/dev/null) + echo ${ret:=$4} +} diff --git a/luci-app-iperf3-server/root/etc/uci-defaults/iperf3-server b/luci-app-iperf3-server/root/etc/uci-defaults/iperf3-server new file mode 100755 index 000000000..f461250fa --- /dev/null +++ b/luci-app-iperf3-server/root/etc/uci-defaults/iperf3-server @@ -0,0 +1,10 @@ +#!/bin/sh + +uci -q batch <<-EOF >/dev/null + delete ucitrack.@iperf3-server[-1] + add ucitrack iperf3-server + set ucitrack.@iperf3-server[-1].init=iperf3-server + commit ucitrack +EOF + +exit 0 \ No newline at end of file diff --git a/luci-app-iperf3-server/root/usr/share/rpcd/acl.d/luci-app-iperf3-server.json b/luci-app-iperf3-server/root/usr/share/rpcd/acl.d/luci-app-iperf3-server.json new file mode 100644 index 000000000..4639e24c5 --- /dev/null +++ b/luci-app-iperf3-server/root/usr/share/rpcd/acl.d/luci-app-iperf3-server.json @@ -0,0 +1,11 @@ +{ + "luci-app-iperf3-server": { + "description": "Grant UCI access for luci-app-iperf3-server", + "read": { + "uci": [ "iperf3-server" ] + }, + "write": { + "uci": [ "iperf3-server" ] + } + } +} diff --git a/luci-app-mosdns/Makefile b/luci-app-mosdns/Makefile new file mode 100644 index 000000000..c1d17ffaa --- /dev/null +++ b/luci-app-mosdns/Makefile @@ -0,0 +1,22 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=luci-app-mosdns +PKG_VERSION:=1.6.16 +PKG_RELEASE:=1 + +LUCI_TITLE:=LuCI Support for mosdns +LUCI_PKGARCH:=all +LUCI_DEPENDS:=+mosdns +jsonfilter +curl +v2ray-geoip +v2ray-geosite +v2dat + +PKG_MAINTAINER:=sbwml + +define Package/$(PKG_NAME)/conffiles +/etc/config/mosdns +/etc/mosdns/cache.dump +/etc/mosdns/config_custom.yaml +/etc/mosdns/rule +endef + +include $(TOPDIR)/feeds/luci/luci.mk + +# call BuildPackage - OpenWrt buildroot signature diff --git a/luci-app-mosdns/htdocs/luci-static/resources/codemirror5/addon/display/autorefresh.min.js b/luci-app-mosdns/htdocs/luci-static/resources/codemirror5/addon/display/autorefresh.min.js new file mode 100644 index 000000000..61e721138 --- /dev/null +++ b/luci-app-mosdns/htdocs/luci-static/resources/codemirror5/addon/display/autorefresh.min.js @@ -0,0 +1 @@ +!function(e){"object"==typeof exports&&"object"==typeof module?e(require("../../lib/codemirror")):"function"==typeof define&&define.amd?define(["../../lib/codemirror"],e):e(CodeMirror)}(function(u){"use strict";function f(e,t){clearTimeout(t.timeout),u.off(window,"mouseup",t.hurry),u.off(window,"keyup",t.hurry)}u.defineOption("autoRefresh",!1,function(e,t){function o(){i.display.wrapper.offsetHeight?(f(0,r),i.display.lastWrapHeight!=i.display.wrapper.clientHeight&&i.refresh()):r.timeout=setTimeout(o,r.delay)}var i,r;e.state.autoRefresh&&(f(0,e.state.autoRefresh),e.state.autoRefresh=null),t&&0==e.display.wrapper.offsetHeight&&((r=(i=e).state.autoRefresh={delay:t.delay||250}).timeout=setTimeout(o,r.delay),r.hurry=function(){clearTimeout(r.timeout),r.timeout=setTimeout(o,50)},u.on(window,"mouseup",r.hurry),u.on(window,"keyup",r.hurry))})}); \ No newline at end of file diff --git a/luci-app-mosdns/htdocs/luci-static/resources/codemirror5/addon/lint/lint.min.css b/luci-app-mosdns/htdocs/luci-static/resources/codemirror5/addon/lint/lint.min.css new file mode 100644 index 000000000..371519ec7 --- /dev/null +++ b/luci-app-mosdns/htdocs/luci-static/resources/codemirror5/addon/lint/lint.min.css @@ -0,0 +1 @@ +.CodeMirror-lint-markers{width:16px}.CodeMirror-lint-tooltip{background-color:#ffd;border:1px solid #000;border-radius:4px 4px 4px 4px;color:#000;font-family:monospace;font-size:10pt;overflow:hidden;padding:2px 5px;position:fixed;white-space:pre;white-space:pre-wrap;z-index:100;max-width:600px;opacity:0;transition:opacity .4s;-moz-transition:opacity .4s;-webkit-transition:opacity .4s;-o-transition:opacity .4s;-ms-transition:opacity .4s}.CodeMirror-lint-mark{background-position:left bottom;background-repeat:repeat-x}.CodeMirror-lint-mark-warning{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJFhQXEbhTg7YAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAMklEQVQI12NkgIIvJ3QXMjAwdDN+OaEbysDA4MPAwNDNwMCwiOHLCd1zX07o6kBVGQEAKBANtobskNMAAAAASUVORK5CYII=)}.CodeMirror-lint-mark-error{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJDw4cOCW1/KIAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAHElEQVQI12NggIL/DAz/GdA5/xkY/qPKMDAwAADLZwf5rvm+LQAAAABJRU5ErkJggg==)}.CodeMirror-lint-marker{background-position:center center;background-repeat:no-repeat;cursor:pointer;display:inline-block;height:16px;width:16px;vertical-align:middle;position:relative}.CodeMirror-lint-message{padding-left:18px;background-position:top left;background-repeat:no-repeat}.CodeMirror-lint-marker-warning,.CodeMirror-lint-message-warning{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAANlBMVEX/uwDvrwD/uwD/uwD/uwD/uwD/uwD/uwD/uwD6twD/uwAAAADurwD2tQD7uAD+ugAAAAD/uwDhmeTRAAAADHRSTlMJ8mN1EYcbmiixgACm7WbuAAAAVklEQVR42n3PUQqAIBBFUU1LLc3u/jdbOJoW1P08DA9Gba8+YWJ6gNJoNYIBzAA2chBth5kLmG9YUoG0NHAUwFXwO9LuBQL1giCQb8gC9Oro2vp5rncCIY8L8uEx5ZkAAAAASUVORK5CYII=)}.CodeMirror-lint-marker-error,.CodeMirror-lint-message-error{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAHlBMVEW7AAC7AACxAAC7AAC7AAAAAAC4AAC5AAD///+7AAAUdclpAAAABnRSTlMXnORSiwCK0ZKSAAAATUlEQVR42mWPOQ7AQAgDuQLx/z8csYRmPRIFIwRGnosRrpamvkKi0FTIiMASR3hhKW+hAN6/tIWhu9PDWiTGNEkTtIOucA5Oyr9ckPgAWm0GPBog6v4AAAAASUVORK5CYII=)}.CodeMirror-lint-marker-multiple{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHCAMAAADzjKfhAAAACVBMVEUAAAAAAAC/v7914kyHAAAAAXRSTlMAQObYZgAAACNJREFUeNo1ioEJAAAIwmz/H90iFFSGJgFMe3gaLZ0od+9/AQZ0ADosbYraAAAAAElFTkSuQmCC);background-repeat:no-repeat;background-position:right bottom;width:100%;height:100%}.CodeMirror-lint-line-error{background-color:rgba(183,76,81,.08)}.CodeMirror-lint-line-warning{background-color:rgba(255,211,0,.1)} \ No newline at end of file diff --git a/luci-app-mosdns/htdocs/luci-static/resources/codemirror5/addon/lint/lint.min.js b/luci-app-mosdns/htdocs/luci-static/resources/codemirror5/addon/lint/lint.min.js new file mode 100644 index 000000000..d9c34d9cb --- /dev/null +++ b/luci-app-mosdns/htdocs/luci-static/resources/codemirror5/addon/lint/lint.min.js @@ -0,0 +1 @@ +!function(t){"object"==typeof exports&&"object"==typeof module?t(require("../../lib/codemirror")):"function"==typeof define&&define.amd?define(["../../lib/codemirror"],t):t(CodeMirror)}(function(p){"use strict";var h="CodeMirror-lint-markers",g="CodeMirror-lint-line-";function u(t){t.parentNode&&t.parentNode.removeChild(t)}function v(t,e,n,o){t=t,e=e,n=n,(i=document.createElement("div")).className="CodeMirror-lint-tooltip cm-s-"+t.options.theme,i.appendChild(n.cloneNode(!0)),(t.state.lint.options.selfContain?t.getWrapperElement():document.body).appendChild(i),p.on(document,"mousemove",a),a(e),null!=i.style.opacity&&(i.style.opacity=1);var i,r=i;function a(t){if(!i.parentNode)return p.off(document,"mousemove",a);var e=Math.max(0,t.clientY-i.offsetHeight-5),t=Math.max(0,Math.min(t.clientX+5,i.ownerDocument.defaultView.innerWidth-i.offsetWidth));i.style.top=e+"px",i.style.left=t+"px"}function l(){var t;p.off(o,"mouseout",l),r&&((t=r).parentNode&&(null==t.style.opacity&&u(t),t.style.opacity=0,setTimeout(function(){u(t)},600)),r=null)}var s=setInterval(function(){if(r)for(var t=o;;t=t.parentNode){if((t=t&&11==t.nodeType?t.host:t)==document.body)return;if(!t){l();break}}if(!r)return clearInterval(s)},400);p.on(o,"mouseout",l)}function a(s,t,e){for(var n in this.marked=[],(t=t instanceof Function?{getAnnotations:t}:t)&&!0!==t||(t={}),this.options={},this.linterOptions=t.options||{},o)this.options[n]=o[n];for(var n in t)o.hasOwnProperty(n)?null!=t[n]&&(this.options[n]=t[n]):t.options||(this.linterOptions[n]=t[n]);this.timeout=null,this.hasGutter=e,this.onMouseOver=function(t){var e=s,n=t.target||t.srcElement;if(/\bCodeMirror-lint-mark-/.test(n.className)){for(var n=n.getBoundingClientRect(),o=(n.left+n.right)/2,n=(n.top+n.bottom)/2,i=e.findMarksAt(e.coordsChar({left:o,top:n},"client")),r=[],a=0;aspan::selection,.cm-fat-cursor .CodeMirror-line>span>span::selection{background:0 0}.cm-fat-cursor .CodeMirror-line::-moz-selection,.cm-fat-cursor .CodeMirror-line>span::-moz-selection,.cm-fat-cursor .CodeMirror-line>span>span::-moz-selection{background:0 0}.cm-fat-cursor{caret-color:transparent}@-moz-keyframes blink{50%{background-color:transparent}}@-webkit-keyframes blink{50%{background-color:transparent}}@keyframes blink{50%{background-color:transparent}}.cm-tab{display:inline-block;text-decoration:inherit}.CodeMirror-rulers{position:absolute;left:0;right:0;top:-50px;bottom:0;overflow:hidden}.CodeMirror-ruler{border-left:1px solid #ccc;top:0;bottom:0;position:absolute}.cm-s-default .cm-header{color:#00f}.cm-s-default .cm-quote{color:#090}.cm-negative{color:#d44}.cm-positive{color:#292}.cm-header,.cm-strong{font-weight:700}.cm-em{font-style:italic}.cm-link{text-decoration:underline}.cm-strikethrough{text-decoration:line-through}.cm-s-default .cm-keyword{color:#708}.cm-s-default .cm-atom{color:#219}.cm-s-default .cm-number{color:#164}.cm-s-default .cm-def{color:#00f}.cm-s-default .cm-variable-2{color:#05a}.cm-s-default .cm-type,.cm-s-default .cm-variable-3{color:#085}.cm-s-default .cm-comment{color:#a50}.cm-s-default .cm-string{color:#a11}.cm-s-default .cm-string-2{color:#f50}.cm-s-default .cm-meta{color:#555}.cm-s-default .cm-qualifier{color:#555}.cm-s-default .cm-builtin{color:#30a}.cm-s-default .cm-bracket{color:#997}.cm-s-default .cm-tag{color:#170}.cm-s-default .cm-attribute{color:#00c}.cm-s-default .cm-hr{color:#999}.cm-s-default .cm-link{color:#00c}.cm-s-default .cm-error{color:red}.cm-invalidchar{color:red}.CodeMirror-composing{border-bottom:2px solid}div.CodeMirror span.CodeMirror-matchingbracket{color:#0b0}div.CodeMirror span.CodeMirror-nonmatchingbracket{color:#a22}.CodeMirror-matchingtag{background:rgba(255,150,0,.3)}.CodeMirror-activeline-background{background:#e8f2ff}.CodeMirror{position:relative;overflow:hidden;background:#fff}.CodeMirror-scroll{overflow:scroll!important;margin-bottom:-50px;margin-right:-50px;padding-bottom:50px;height:100%;outline:0;position:relative;z-index:0}.CodeMirror-sizer{position:relative;border-right:50px solid transparent}.CodeMirror-gutter-filler,.CodeMirror-hscrollbar,.CodeMirror-scrollbar-filler,.CodeMirror-vscrollbar{position:absolute;z-index:6;display:none;outline:0}.CodeMirror-vscrollbar{right:0;top:0;overflow-x:hidden;overflow-y:scroll}.CodeMirror-hscrollbar{bottom:0;left:0;overflow-y:hidden;overflow-x:scroll}.CodeMirror-scrollbar-filler{right:0;bottom:0}.CodeMirror-gutter-filler{left:0;bottom:0}.CodeMirror-gutters{position:absolute;left:0;top:0;min-height:100%;z-index:3}.CodeMirror-gutter{white-space:normal;height:100%;display:inline-block;vertical-align:top;margin-bottom:-50px}.CodeMirror-gutter-wrapper{position:absolute;z-index:4;background:0 0!important;border:none!important}.CodeMirror-gutter-background{position:absolute;top:0;bottom:0;z-index:4}.CodeMirror-gutter-elt{position:absolute;cursor:default;z-index:4}.CodeMirror-gutter-wrapper ::selection{background-color:transparent}.CodeMirror-gutter-wrapper ::-moz-selection{background-color:transparent}.CodeMirror-lines{cursor:text;min-height:1px}.CodeMirror pre.CodeMirror-line,.CodeMirror pre.CodeMirror-line-like{-moz-border-radius:0;-webkit-border-radius:0;border-radius:0;border-width:0;background:0 0;font-family:Arial,monospace;font-size:15px;margin:0;white-space:pre;word-wrap:normal;line-height:1.25;color:inherit;z-index:2;position:relative;overflow:visible;-webkit-tap-highlight-color:transparent;-webkit-font-variant-ligatures:contextual;font-variant-ligatures:contextual}.CodeMirror-wrap pre.CodeMirror-line,.CodeMirror-wrap pre.CodeMirror-line-like{word-wrap:break-word;white-space:pre-wrap;word-break:normal}.CodeMirror-linebackground{position:absolute;left:0;right:0;top:0;bottom:0;z-index:0}.CodeMirror-linewidget{position:relative;z-index:2;padding:.1px}.CodeMirror-rtl pre{direction:rtl}.CodeMirror-code{outline:0}.CodeMirror-gutter,.CodeMirror-gutters,.CodeMirror-linenumber,.CodeMirror-scroll,.CodeMirror-sizer{-moz-box-sizing:content-box;box-sizing:content-box}.CodeMirror-measure{position:absolute;width:100%;height:0;overflow:hidden;visibility:hidden}.CodeMirror-cursor{position:absolute;pointer-events:none}.CodeMirror-measure pre{position:static}div.CodeMirror-cursors{visibility:hidden;position:relative;z-index:3}div.CodeMirror-dragcursors{visibility:visible}.CodeMirror-focused div.CodeMirror-cursors{visibility:visible}.CodeMirror-selected{background:#d9d9d9}.CodeMirror-focused .CodeMirror-selected{background:#d7d4f0}.CodeMirror-crosshair{cursor:crosshair}.CodeMirror-line::selection,.CodeMirror-line>span::selection,.CodeMirror-line>span>span::selection{background:#d7d4f0}.CodeMirror-line::-moz-selection,.CodeMirror-line>span::-moz-selection,.CodeMirror-line>span>span::-moz-selection{background:#d7d4f0}.cm-searching{background-color:#ffa;background-color:rgba(255,255,0,.4)}.cm-force-border{padding-right:.1px}@media print{.CodeMirror div.CodeMirror-cursors{visibility:hidden}}.cm-tab-wrap-hack:after{content:''}span.CodeMirror-selectedtext{background:0 0}span{cursor:unset!important} \ No newline at end of file diff --git a/luci-app-mosdns/htdocs/luci-static/resources/codemirror5/codemirror.min.js b/luci-app-mosdns/htdocs/luci-static/resources/codemirror5/codemirror.min.js new file mode 100644 index 000000000..213ce392b --- /dev/null +++ b/luci-app-mosdns/htdocs/luci-static/resources/codemirror5/codemirror.min.js @@ -0,0 +1 @@ +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):e.CodeMirror=t()}(this,function(){"use strict";var e=navigator.userAgent,t=navigator.platform,r=/gecko\/\d/i.test(e),n=/MSIE \d/.test(e),i=/Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(e),o=/Edge\/(\d+)/.exec(e),l=n||i||o,s=l&&(n?document.documentMode||6:+(o||i)[1]),a=!o&&/WebKit\//.test(e),u=a&&/Qt\/\d+\.\d+/.test(e),c=!o&&/Chrome\//.test(e),h=/Opera\//.test(e),f=/Apple Computer/.test(navigator.vendor),d=/Mac OS X 1\d\D([8-9]|\d\d)\D/.test(e),p=/PhantomJS/.test(e),g=!o&&/AppleWebKit/.test(e)&&/Mobile\/\w+/.test(e),v=/Android/.test(e),m=g||v||/webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(e),y=g||/Mac/.test(t),b=/\bCrOS\b/.test(e),w=/win/i.test(t),x=h&&e.match(/Version\/(\d*\.\d*)/);x&&(x=Number(x[1])),x&&x>=15&&(h=!1,a=!0);var C=y&&(u||h&&(null==x||x<12.11)),S=r||l&&s>=9;function L(e){return new RegExp("(^|\\s)"+e+"(?:$|\\s)\\s*")}var k,T=function(e,t){var r=e.className,n=L(t).exec(r);if(n){var i=r.slice(n.index+n[0].length);e.className=r.slice(0,n.index)+(i?n[1]+i:"")}};function M(e){for(var t=e.childNodes.length;t>0;--t)e.removeChild(e.firstChild);return e}function N(e,t){return M(e).appendChild(t)}function O(e,t,r,n){var i=document.createElement(e);if(r&&(i.className=r),n&&(i.style.cssText=n),"string"==typeof t)i.appendChild(document.createTextNode(t));else if(t)for(var o=0;o=t)return l+(t-o);l+=s-o,l+=r-l%r,o=s+1}}g?P=function(e){e.selectionStart=0,e.selectionEnd=e.value.length}:l&&(P=function(e){try{e.select()}catch(e){}});var R=function(){this.id=null,this.f=null,this.time=0,this.handler=E(this.onTimeout,this)};function B(e,t){for(var r=0;r=t)return n+Math.min(l,t-i);if(i+=o-n,n=o+1,(i+=r-i%r)>=t)return n}}var Y=[""];function _(e){for(;Y.length<=e;)Y.push($(Y)+" ");return Y[e]}function $(e){return e[e.length-1]}function q(e,t){for(var r=[],n=0;n"€"&&(e.toUpperCase()!=e.toLowerCase()||J.test(e))}function te(e,t){return t?!!(t.source.indexOf("\\w")>-1&&ee(e))||t.test(e):ee(e)}function re(e){for(var t in e)if(e.hasOwnProperty(t)&&e[t])return!1;return!0}var ne=/[\u0300-\u036f\u0483-\u0489\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u065e\u0670\u06d6-\u06dc\u06de-\u06e4\u06e7\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0900-\u0902\u093c\u0941-\u0948\u094d\u0951-\u0955\u0962\u0963\u0981\u09bc\u09be\u09c1-\u09c4\u09cd\u09d7\u09e2\u09e3\u0a01\u0a02\u0a3c\u0a41\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a70\u0a71\u0a75\u0a81\u0a82\u0abc\u0ac1-\u0ac5\u0ac7\u0ac8\u0acd\u0ae2\u0ae3\u0b01\u0b3c\u0b3e\u0b3f\u0b41-\u0b44\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b82\u0bbe\u0bc0\u0bcd\u0bd7\u0c3e-\u0c40\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0cbc\u0cbf\u0cc2\u0cc6\u0ccc\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0d3e\u0d41-\u0d44\u0d4d\u0d57\u0d62\u0d63\u0dca\u0dcf\u0dd2-\u0dd4\u0dd6\u0ddf\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0f18\u0f19\u0f35\u0f37\u0f39\u0f71-\u0f7e\u0f80-\u0f84\u0f86\u0f87\u0f90-\u0f97\u0f99-\u0fbc\u0fc6\u102d-\u1030\u1032-\u1037\u1039\u103a\u103d\u103e\u1058\u1059\u105e-\u1060\u1071-\u1074\u1082\u1085\u1086\u108d\u109d\u135f\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b7-\u17bd\u17c6\u17c9-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u1922\u1927\u1928\u1932\u1939-\u193b\u1a17\u1a18\u1a56\u1a58-\u1a5e\u1a60\u1a62\u1a65-\u1a6c\u1a73-\u1a7c\u1a7f\u1b00-\u1b03\u1b34\u1b36-\u1b3a\u1b3c\u1b42\u1b6b-\u1b73\u1b80\u1b81\u1ba2-\u1ba5\u1ba8\u1ba9\u1c2c-\u1c33\u1c36\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce0\u1ce2-\u1ce8\u1ced\u1dc0-\u1de6\u1dfd-\u1dff\u200c\u200d\u20d0-\u20f0\u2cef-\u2cf1\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua66f-\ua672\ua67c\ua67d\ua6f0\ua6f1\ua802\ua806\ua80b\ua825\ua826\ua8c4\ua8e0-\ua8f1\ua926-\ua92d\ua947-\ua951\ua980-\ua982\ua9b3\ua9b6-\ua9b9\ua9bc\uaa29-\uaa2e\uaa31\uaa32\uaa35\uaa36\uaa43\uaa4c\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uabe5\uabe8\uabed\udc00-\udfff\ufb1e\ufe00-\ufe0f\ufe20-\ufe26\uff9e\uff9f]/;function ie(e){return e.charCodeAt(0)>=768&&ne.test(e)}function oe(e,t,r){for(;(r<0?t>0:tr?-1:1;;){if(t==r)return t;var i=(t+r)/2,o=n<0?Math.ceil(i):Math.floor(i);if(o==t)return e(o)?t:r;e(o)?r=o:t=o+n}}var se=null;function ae(e,t,r){var n;se=null;for(var i=0;it)return i;o.to==t&&(o.from!=o.to&&"before"==r?n=i:se=i),o.from==t&&(o.from!=o.to&&"before"!=r?n=i:se=i)}return null!=n?n:se}var ue=function(){var e="bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN",t="nnnnnnNNr%%r,rNNmmmmmmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmmmnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmnNmmmmmmrrmmNmmmmrr1111111111";var r=/[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/,n=/[stwN]/,i=/[LRr]/,o=/[Lb1n]/,l=/[1n]/;function s(e,t,r){this.level=e,this.from=t,this.to=r}return function(a,u){var c="ltr"==u?"L":"R";if(0==a.length||"ltr"==u&&!r.test(a))return!1;for(var h,f=a.length,d=[],p=0;p-1&&(n[t]=i.slice(0,o).concat(i.slice(o+1)))}}}function ge(e,t){var r=de(e,t);if(r.length)for(var n=Array.prototype.slice.call(arguments,2),i=0;i0}function be(e){e.prototype.on=function(e,t){fe(this,e,t)},e.prototype.off=function(e,t){pe(this,e,t)}}function we(e){e.preventDefault?e.preventDefault():e.returnValue=!1}function xe(e){e.stopPropagation?e.stopPropagation():e.cancelBubble=!0}function Ce(e){return null!=e.defaultPrevented?e.defaultPrevented:0==e.returnValue}function Se(e){we(e),xe(e)}function Le(e){return e.target||e.srcElement}function ke(e){var t=e.which;return null==t&&(1&e.button?t=1:2&e.button?t=3:4&e.button&&(t=2)),y&&e.ctrlKey&&1==t&&(t=3),t}var Te,Me,Ne=function(){if(l&&s<9)return!1;var e=O("div");return"draggable"in e||"dragDrop"in e}();function Oe(e){if(null==Te){var t=O("span","​");N(e,O("span",[t,document.createTextNode("x")])),0!=e.firstChild.offsetHeight&&(Te=t.offsetWidth<=1&&t.offsetHeight>2&&!(l&&s<8))}var r=Te?O("span","​"):O("span"," ",null,"display: inline-block; width: 1px; margin-right: -1px");return r.setAttribute("cm-text",""),r}function Ae(e){if(null!=Me)return Me;var t=N(e,document.createTextNode("AخA")),r=k(t,0,1).getBoundingClientRect(),n=k(t,1,2).getBoundingClientRect();return M(e),!(!r||r.left==r.right)&&(Me=n.right-r.right<3)}var De,We=3!="\n\nb".split(/\n/).length?function(e){for(var t=0,r=[],n=e.length;t<=n;){var i=e.indexOf("\n",t);-1==i&&(i=e.length);var o=e.slice(t,"\r"==e.charAt(i-1)?i-1:i),l=o.indexOf("\r");-1!=l?(r.push(o.slice(0,l)),t+=l+1):(r.push(o),t=i+1)}return r}:function(e){return e.split(/\r\n?|\n/)},He=window.getSelection?function(e){try{return e.selectionStart!=e.selectionEnd}catch(e){return!1}}:function(e){var t;try{t=e.ownerDocument.selection.createRange()}catch(e){}return!(!t||t.parentElement()!=e)&&0!=t.compareEndPoints("StartToEnd",t)},Fe="oncopy"in(De=O("div"))||(De.setAttribute("oncopy","return;"),"function"==typeof De.oncopy),Pe=null;var Ee={},Ie={};function ze(e){if("string"==typeof e&&Ie.hasOwnProperty(e))e=Ie[e];else if(e&&"string"==typeof e.name&&Ie.hasOwnProperty(e.name)){var t=Ie[e.name];"string"==typeof t&&(t={name:t}),(e=Q(t,e)).name=t.name}else{if("string"==typeof e&&/^[\w\-]+\/[\w\-]+\+xml$/.test(e))return ze("application/xml");if("string"==typeof e&&/^[\w\-]+\/[\w\-]+\+json$/.test(e))return ze("application/json")}return"string"==typeof e?{name:e}:e||{name:"null"}}function Re(e,t){t=ze(t);var r=Ee[t.name];if(!r)return Re(e,"text/plain");var n=r(e,t);if(Be.hasOwnProperty(t.name)){var i=Be[t.name];for(var o in i)i.hasOwnProperty(o)&&(n.hasOwnProperty(o)&&(n["_"+o]=n[o]),n[o]=i[o])}if(n.name=t.name,t.helperType&&(n.helperType=t.helperType),t.modeProps)for(var l in t.modeProps)n[l]=t.modeProps[l];return n}var Be={};function Ge(e,t){I(t,Be.hasOwnProperty(e)?Be[e]:Be[e]={})}function Ue(e,t){if(!0===t)return t;if(e.copyState)return e.copyState(t);var r={};for(var n in t){var i=t[n];i instanceof Array&&(i=i.concat([])),r[n]=i}return r}function Ve(e,t){for(var r;e.innerMode&&(r=e.innerMode(t))&&r.mode!=e;)t=r.state,e=r.mode;return r||{mode:e,state:t}}function Ke(e,t,r){return!e.startState||e.startState(t,r)}var je=function(e,t,r){this.pos=this.start=0,this.string=e,this.tabSize=t||8,this.lastColumnPos=this.lastColumnValue=0,this.lineStart=0,this.lineOracle=r};function Xe(e,t){if((t-=e.first)<0||t>=e.size)throw new Error("There is no line "+(t+e.first)+" in the document.");for(var r=e;!r.lines;)for(var n=0;;++n){var i=r.children[n],o=i.chunkSize();if(t=e.first&&tr?et(r,Xe(e,r).text.length):function(e,t){var r=e.ch;return null==r||r>t?et(e.line,t):r<0?et(e.line,0):e}(t,Xe(e,t.line).text.length)}function at(e,t){for(var r=[],n=0;n=this.string.length},je.prototype.sol=function(){return this.pos==this.lineStart},je.prototype.peek=function(){return this.string.charAt(this.pos)||void 0},je.prototype.next=function(){if(this.post},je.prototype.eatSpace=function(){for(var e=this.pos;/[\s\u00a0]/.test(this.string.charAt(this.pos));)++this.pos;return this.pos>e},je.prototype.skipToEnd=function(){this.pos=this.string.length},je.prototype.skipTo=function(e){var t=this.string.indexOf(e,this.pos);if(t>-1)return this.pos=t,!0},je.prototype.backUp=function(e){this.pos-=e},je.prototype.column=function(){return this.lastColumnPos0?null:(n&&!1!==t&&(this.pos+=n[0].length),n)}var i=function(e){return r?e.toLowerCase():e};if(i(this.string.substr(this.pos,e.length))==i(e))return!1!==t&&(this.pos+=e.length),!0},je.prototype.current=function(){return this.string.slice(this.start,this.pos)},je.prototype.hideFirstChars=function(e,t){this.lineStart+=e;try{return t()}finally{this.lineStart-=e}},je.prototype.lookAhead=function(e){var t=this.lineOracle;return t&&t.lookAhead(e)},je.prototype.baseToken=function(){var e=this.lineOracle;return e&&e.baseToken(this.pos)};var ut=function(e,t){this.state=e,this.lookAhead=t},ct=function(e,t,r,n){this.state=t,this.doc=e,this.line=r,this.maxLookAhead=n||0,this.baseTokens=null,this.baseTokenPos=1};function ht(e,t,r,n){var i=[e.state.modeGen],o={};wt(e,t.text,e.doc.mode,r,function(e,t){return i.push(e,t)},o,n);for(var l=r.state,s=function(n){r.baseTokens=i;var s=e.state.overlays[n],a=1,u=0;r.state=!0,wt(e,t.text,s.mode,r,function(e,t){for(var r=a;ue&&i.splice(a,1,e,i[a+1],n),a+=2,u=Math.min(e,n)}if(t)if(s.opaque)i.splice(r,a-r,e,"overlay "+t),a=r+2;else for(;re.options.maxHighlightLength&&Ue(e.doc.mode,n.state),o=ht(e,t,n);i&&(n.state=i),t.stateAfter=n.save(!i),t.styles=o.styles,o.classes?t.styleClasses=o.classes:t.styleClasses&&(t.styleClasses=null),r===e.doc.highlightFrontier&&(e.doc.modeFrontier=Math.max(e.doc.modeFrontier,++e.doc.highlightFrontier))}return t.styles}function dt(e,t,r){var n=e.doc,i=e.display;if(!n.mode.startState)return new ct(n,!0,t);var o=function(e,t,r){for(var n,i,o=e.doc,l=r?-1:t-(e.doc.mode.innerMode?1e3:100),s=t;s>l;--s){if(s<=o.first)return o.first;var a=Xe(o,s-1),u=a.stateAfter;if(u&&(!r||s+(u instanceof ut?u.lookAhead:0)<=o.modeFrontier))return s;var c=z(a.text,null,e.options.tabSize);(null==i||n>c)&&(i=s-1,n=c)}return i}(e,t,r),l=o>n.first&&Xe(n,o-1).stateAfter,s=l?ct.fromSaved(n,l,o):new ct(n,Ke(n.mode),o);return n.iter(o,t,function(r){pt(e,r.text,s);var n=s.line;r.stateAfter=n==t-1||n%5==0||n>=i.viewFrom&&nt.start)return o}throw new Error("Mode "+e.name+" failed to advance stream.")}ct.prototype.lookAhead=function(e){var t=this.doc.getLine(this.line+e);return null!=t&&e>this.maxLookAhead&&(this.maxLookAhead=e),t},ct.prototype.baseToken=function(e){if(!this.baseTokens)return null;for(;this.baseTokens[this.baseTokenPos]<=e;)this.baseTokenPos+=2;var t=this.baseTokens[this.baseTokenPos+1];return{type:t&&t.replace(/( |^)overlay .*/,""),size:this.baseTokens[this.baseTokenPos]-e}},ct.prototype.nextLine=function(){this.line++,this.maxLookAhead>0&&this.maxLookAhead--},ct.fromSaved=function(e,t,r){return t instanceof ut?new ct(e,Ue(e.mode,t.state),r,t.lookAhead):new ct(e,Ue(e.mode,t),r)},ct.prototype.save=function(e){var t=!1!==e?Ue(this.doc.mode,this.state):this.state;return this.maxLookAhead>0?new ut(t,this.maxLookAhead):t};var mt=function(e,t,r){this.start=e.start,this.end=e.pos,this.string=e.current(),this.type=t||null,this.state=r};function yt(e,t,r,n){var i,o,l=e.doc,s=l.mode,a=Xe(l,(t=st(l,t)).line),u=dt(e,t.line,r),c=new je(a.text,e.options.tabSize,u);for(n&&(o=[]);(n||c.pose.options.maxHighlightLength?(s=!1,l&&pt(e,t,n,h.pos),h.pos=t.length,a=null):a=bt(vt(r,h,n.state,f),o),f){var d=f[0].name;d&&(a="m-"+(a?d+" "+a:d))}if(!s||c!=a){for(;u=t:o.to>t);(n||(n=[])).push(new St(l,o.from,s?null:o.to))}}return n}(r,i,l),a=function(e,t,r){var n;if(e)for(var i=0;i=t:o.to>t)||o.from==t&&"bookmark"==l.type&&(!r||o.marker.insertLeft)){var s=null==o.from||(l.inclusiveLeft?o.from<=t:o.from0&&s)for(var b=0;bt)&&(!r||Wt(r,o.marker)<0)&&(r=o.marker)}return r}function It(e,t,r,n,i){var o=Xe(e,t),l=Ct&&o.markedSpans;if(l)for(var s=0;s=0&&h<=0||c<=0&&h>=0)&&(c<=0&&(a.marker.inclusiveRight&&i.inclusiveLeft?tt(u.to,r)>=0:tt(u.to,r)>0)||c>=0&&(a.marker.inclusiveRight&&i.inclusiveLeft?tt(u.from,n)<=0:tt(u.from,n)<0)))return!0}}}function zt(e){for(var t;t=Ft(e);)e=t.find(-1,!0).line;return e}function Rt(e,t){var r=Xe(e,t),n=zt(r);return r==n?t:qe(n)}function Bt(e,t){if(t>e.lastLine())return t;var r,n=Xe(e,t);if(!Gt(e,n))return t;for(;r=Pt(n);)n=r.find(1,!0).line;return qe(n)+1}function Gt(e,t){var r=Ct&&t.markedSpans;if(r)for(var n=void 0,i=0;it.maxLineLength&&(t.maxLineLength=r,t.maxLine=e)})}var Xt=function(e,t,r){this.text=e,Ot(this,t),this.height=r?r(this):1};function Yt(e){e.parent=null,Nt(e)}Xt.prototype.lineNo=function(){return qe(this)},be(Xt);var _t={},$t={};function qt(e,t){if(!e||/^\s*$/.test(e))return null;var r=t.addModeClass?$t:_t;return r[e]||(r[e]=e.replace(/\S+/g,"cm-$&"))}function Zt(e,t){var r=A("span",null,null,a?"padding-right: .1px":null),n={pre:A("pre",[r],"CodeMirror-line"),content:r,col:0,pos:0,cm:e,trailingSpace:!1,splitSpaces:e.getOption("lineWrapping")};t.measure={};for(var i=0;i<=(t.rest?t.rest.length:0);i++){var o=i?t.rest[i-1]:t.line,l=void 0;n.pos=0,n.addToken=Jt,Ae(e.display.measure)&&(l=ce(o,e.doc.direction))&&(n.addToken=er(n.addToken,l)),n.map=[],rr(o,n,ft(e,o,t!=e.display.externalMeasured&&qe(o))),o.styleClasses&&(o.styleClasses.bgClass&&(n.bgClass=F(o.styleClasses.bgClass,n.bgClass||"")),o.styleClasses.textClass&&(n.textClass=F(o.styleClasses.textClass,n.textClass||""))),0==n.map.length&&n.map.push(0,0,n.content.appendChild(Oe(e.display.measure))),0==i?(t.measure.map=n.map,t.measure.cache={}):((t.measure.maps||(t.measure.maps=[])).push(n.map),(t.measure.caches||(t.measure.caches=[])).push({}))}if(a){var s=n.content.lastChild;(/\bcm-tab\b/.test(s.className)||s.querySelector&&s.querySelector(".cm-tab"))&&(n.content.className="cm-tab-wrap-hack")}return ge(e,"renderLine",e,t.line,n.pre),n.pre.className&&(n.textClass=F(n.pre.className,n.textClass||"")),n}function Qt(e){var t=O("span","•","cm-invalidchar");return t.title="\\u"+e.charCodeAt(0).toString(16),t.setAttribute("aria-label",t.title),t}function Jt(e,t,r,n,i,o,a){if(t){var u,c=e.splitSpaces?function(e,t){if(e.length>1&&!/ /.test(e))return e;for(var r=t,n="",i=0;iu&&h.from<=u);f++);if(h.to>=c)return e(r,n,i,o,l,s,a);e(r,n.slice(0,h.to-u),i,o,null,s,a),o=null,n=n.slice(h.to-u),u=h.to}}}function tr(e,t,r,n){var i=!n&&r.widgetNode;i&&e.map.push(e.pos,e.pos+t,i),!n&&e.cm.display.input.needsContentAttribute&&(i||(i=e.content.appendChild(document.createElement("span"))),i.setAttribute("cm-marker",r.id)),i&&(e.cm.display.input.setUneditable(i),e.content.appendChild(i)),e.pos+=t,e.trailingSpace=!1}function rr(e,t,r){var n=e.markedSpans,i=e.text,o=0;if(n)for(var l,s,a,u,c,h,f,d=i.length,p=0,g=1,v="",m=0;;){if(m==p){a=u=c=s="",f=null,h=null,m=1/0;for(var y=[],b=void 0,w=0;wp||C.collapsed&&x.to==p&&x.from==p)){if(null!=x.to&&x.to!=p&&m>x.to&&(m=x.to,u=""),C.className&&(a+=" "+C.className),C.css&&(s=(s?s+";":"")+C.css),C.startStyle&&x.from==p&&(c+=" "+C.startStyle),C.endStyle&&x.to==m&&(b||(b=[])).push(C.endStyle,x.to),C.title&&((f||(f={})).title=C.title),C.attributes)for(var S in C.attributes)(f||(f={}))[S]=C.attributes[S];C.collapsed&&(!h||Wt(h.marker,C)<0)&&(h=x)}else x.from>p&&m>x.from&&(m=x.from)}if(b)for(var L=0;L=d)break;for(var T=Math.min(d,m);;){if(v){var M=p+v.length;if(!h){var N=M>T?v.slice(0,T-p):v;t.addToken(t,N,l?l+a:a,c,p+N.length==m?u:"",s,f)}if(M>=T){v=v.slice(T-p),p=T;break}p=M,c=""}v=i.slice(o,o=r[g++]),l=qt(r[g++],t.cm.options)}}else for(var O=1;Or)return{map:e.measure.maps[i],cache:e.measure.caches[i],before:!0}}function Or(e,t,r,n){return Wr(e,Dr(e,t),r,n)}function Ar(e,t){if(t>=e.display.viewFrom&&t=r.lineN&&t2&&o.push((a.bottom+u.top)/2-r.top)}}o.push(r.bottom-r.top)}}(e,t.view,t.rect),t.hasHeights=!0),(o=function(e,t,r,n){var i,o=Pr(t.map,r,n),a=o.node,u=o.start,c=o.end,h=o.collapse;if(3==a.nodeType){for(var f=0;f<4;f++){for(;u&&ie(t.line.text.charAt(o.coverStart+u));)--u;for(;o.coverStart+c1}(e))return t;var r=screen.logicalXDPI/screen.deviceXDPI,n=screen.logicalYDPI/screen.deviceYDPI;return{left:t.left*r,right:t.right*r,top:t.top*n,bottom:t.bottom*n}}(e.display.measure,i))}else{var d;u>0&&(h=n="right"),i=e.options.lineWrapping&&(d=a.getClientRects()).length>1?d["right"==n?d.length-1:0]:a.getBoundingClientRect()}if(l&&s<9&&!u&&(!i||!i.left&&!i.right)){var p=a.parentNode.getClientRects()[0];i=p?{left:p.left,right:p.left+tn(e.display),top:p.top,bottom:p.bottom}:Fr}for(var g=i.top-t.rect.top,v=i.bottom-t.rect.top,m=(g+v)/2,y=t.view.measure.heights,b=0;bt)&&(i=(o=a-s)-1,t>=a&&(l="right")),null!=i){if(n=e[u+2],s==a&&r==(n.insertLeft?"left":"right")&&(l=r),"left"==r&&0==i)for(;u&&e[u-2]==e[u-3]&&e[u-1].insertLeft;)n=e[2+(u-=3)],l="left";if("right"==r&&i==a-s)for(;u=0&&(r=e[i]).left==r.right;i--);return r}function Ir(e){if(e.measure&&(e.measure.cache={},e.measure.heights=null,e.rest))for(var t=0;t=n.text.length?(a=n.text.length,u="before"):a<=0&&(a=0,u="after"),!s)return l("before"==u?a-1:a,"before"==u);function c(e,t,r){return l(r?e-1:e,1==s[t].level!=r)}var h=ae(s,a,u),f=se,d=c(a,h,"before"==u);return null!=f&&(d.other=c(a,f,"before"!=u)),d}function Yr(e,t){var r=0;t=st(e.doc,t),e.options.lineWrapping||(r=tn(e.display)*t.ch);var n=Xe(e.doc,t.line),i=Vt(n)+Cr(e.display);return{left:r,right:r,top:i,bottom:i+n.height}}function _r(e,t,r,n,i){var o=et(e,t,r);return o.xRel=i,n&&(o.outside=n),o}function $r(e,t,r){var n=e.doc;if((r+=e.display.viewOffset)<0)return _r(n.first,0,null,-1,-1);var i=Ze(n,r),o=n.first+n.size-1;if(i>o)return _r(n.first+n.size-1,Xe(n,o).text.length,null,1,1);t<0&&(t=0);for(var l=Xe(n,i);;){var s=Jr(e,l,i,t,r),a=Et(l,s.ch+(s.xRel>0||s.outside>0?1:0));if(!a)return s;var u=a.find(1);if(u.line==i)return u;l=Xe(n,i=u.line)}}function qr(e,t,r,n){n-=Ur(t);var i=t.text.length,o=le(function(t){return Wr(e,r,t-1).bottom<=n},i,0);return{begin:o,end:i=le(function(t){return Wr(e,r,t).top>n},o,i)}}function Zr(e,t,r,n){return r||(r=Dr(e,t)),qr(e,t,r,Vr(e,t,Wr(e,r,n),"line").top)}function Qr(e,t,r,n){return!(e.bottom<=r)&&(e.top>r||(n?e.left:e.right)>t)}function Jr(e,t,r,n,i){i-=Vt(t);var o=Dr(e,t),l=Ur(t),s=0,a=t.text.length,u=!0,c=ce(t,e.doc.direction);if(c){var h=(e.options.lineWrapping?function(e,t,r,n,i,o,l){var s=qr(e,t,n,l),a=s.begin,u=s.end;/\s/.test(t.text.charAt(u-1))&&u--;for(var c=null,h=null,f=0;f=u||d.to<=a)){var p=1!=d.level,g=Wr(e,n,p?Math.min(u,d.to)-1:Math.max(a,d.from)).right,v=gv)&&(c=d,h=v)}}c||(c=i[i.length-1]);c.fromu&&(c={from:c.from,to:u,level:c.level});return c}:function(e,t,r,n,i,o,l){var s=le(function(s){var a=i[s],u=1!=a.level;return Qr(Xr(e,et(r,u?a.to:a.from,u?"before":"after"),"line",t,n),o,l,!0)},0,i.length-1),a=i[s];if(s>0){var u=1!=a.level,c=Xr(e,et(r,u?a.from:a.to,u?"after":"before"),"line",t,n);Qr(c,o,l,!0)&&c.top>l&&(a=i[s-1])}return a})(e,t,r,o,c,n,i);s=(u=1!=h.level)?h.from:h.to-1,a=u?h.to:h.from-1}var f,d,p=null,g=null,v=le(function(t){var r=Wr(e,o,t);return r.top+=l,r.bottom+=l,!!Qr(r,n,i,!1)&&(r.top<=i&&r.left<=n&&(p=t,g=r),!0)},s,a),m=!1;if(g){var y=n-g.left=w.bottom?1:0}return _r(r,v=oe(t.text,v,1),d,m,n-f)}function en(e){if(null!=e.cachedTextHeight)return e.cachedTextHeight;if(null==Hr){Hr=O("pre",null,"CodeMirror-line-like");for(var t=0;t<49;++t)Hr.appendChild(document.createTextNode("x")),Hr.appendChild(O("br"));Hr.appendChild(document.createTextNode("x"))}N(e.measure,Hr);var r=Hr.offsetHeight/50;return r>3&&(e.cachedTextHeight=r),M(e.measure),r||1}function tn(e){if(null!=e.cachedCharWidth)return e.cachedCharWidth;var t=O("span","xxxxxxxxxx"),r=O("pre",[t],"CodeMirror-line-like");N(e.measure,r);var n=t.getBoundingClientRect(),i=(n.right-n.left)/10;return i>2&&(e.cachedCharWidth=i),i||10}function rn(e){for(var t=e.display,r={},n={},i=t.gutters.clientLeft,o=t.gutters.firstChild,l=0;o;o=o.nextSibling,++l){var s=e.display.gutterSpecs[l].className;r[s]=o.offsetLeft+o.clientLeft+i,n[s]=o.clientWidth}return{fixedPos:nn(t),gutterTotalWidth:t.gutters.offsetWidth,gutterLeft:r,gutterWidth:n,wrapperWidth:t.wrapper.clientWidth}}function nn(e){return e.scroller.getBoundingClientRect().left-e.sizer.getBoundingClientRect().left}function on(e){var t=en(e.display),r=e.options.lineWrapping,n=r&&Math.max(5,e.display.scroller.clientWidth/tn(e.display)-3);return function(i){if(Gt(e.doc,i))return 0;var o=0;if(i.widgets)for(var l=0;l=e.display.viewTo)return null;if((t-=e.display.viewFrom)<0)return null;for(var r=e.display.view,n=0;nt)&&(i.updateLineNumbers=t),e.curOp.viewChanged=!0,t>=i.viewTo)Ct&&Rt(e.doc,t)i.viewFrom?hn(e):(i.viewFrom+=n,i.viewTo+=n);else if(t<=i.viewFrom&&r>=i.viewTo)hn(e);else if(t<=i.viewFrom){var o=fn(e,r,r+n,1);o?(i.view=i.view.slice(o.index),i.viewFrom=o.lineN,i.viewTo+=n):hn(e)}else if(r>=i.viewTo){var l=fn(e,t,t,-1);l?(i.view=i.view.slice(0,l.index),i.viewTo=l.lineN):hn(e)}else{var s=fn(e,t,t,-1),a=fn(e,r,r+n,1);s&&a?(i.view=i.view.slice(0,s.index).concat(ir(e,s.lineN,a.lineN)).concat(i.view.slice(a.index)),i.viewTo+=n):hn(e)}var u=i.externalMeasured;u&&(r=i.lineN&&t=n.viewTo)){var o=n.view[an(e,t)];if(null!=o.node){var l=o.changes||(o.changes=[]);-1==B(l,r)&&l.push(r)}}}function hn(e){e.display.viewFrom=e.display.viewTo=e.doc.first,e.display.view=[],e.display.viewOffset=0}function fn(e,t,r,n){var i,o=an(e,t),l=e.display.view;if(!Ct||r==e.doc.first+e.doc.size)return{index:o,lineN:r};for(var s=e.display.viewFrom,a=0;a0){if(o==l.length-1)return null;i=s+l[o].size-t,o++}else i=s-t;t+=i,r+=i}for(;Rt(e.doc,r)!=r;){if(o==(n<0?0:l.length-1))return null;r+=n*l[o-(n<0?1:0)].size,o+=n}return{index:o,lineN:r}}function dn(e){for(var t=e.display.view,r=0,n=0;n=e.display.viewTo||s.to().linet||t==r&&l.to==t)&&(n(Math.max(l.from,t),Math.min(l.to,r),1==l.level?"rtl":"ltr",o),i=!0)}i||n(t,r,"ltr")}(g,r||0,null==n?f:n,function(e,t,i,h){var v="ltr"==i,m=d(e,v?"left":"right"),y=d(t-1,v?"right":"left"),b=null==r&&0==e,w=null==n&&t==f,x=0==h,C=!g||h==g.length-1;if(y.top-m.top<=3){var S=(u?w:b)&&C,L=(u?b:w)&&x?s:(v?m:y).left,k=S?a:(v?y:m).right;c(L,m.top,k-L,m.bottom)}else{var T,M,N,O;v?(T=u&&b&&x?s:m.left,M=u?a:p(e,i,"before"),N=u?s:p(t,i,"after"),O=u&&w&&C?a:y.right):(T=u?p(e,i,"before"):s,M=!u&&b&&x?a:m.right,N=!u&&w&&C?s:y.left,O=u?p(t,i,"after"):a),c(T,m.top,M-T,m.bottom),m.bottom0?t.blinker=setInterval(function(){return t.cursorDiv.style.visibility=(r=!r)?"":"hidden"},e.options.cursorBlinkRate):e.options.cursorBlinkRate<0&&(t.cursorDiv.style.visibility="hidden")}}function wn(e){e.state.focused||(e.display.input.focus(),Cn(e))}function xn(e){e.state.delayingBlurEvent=!0,setTimeout(function(){e.state.delayingBlurEvent&&(e.state.delayingBlurEvent=!1,Sn(e))},100)}function Cn(e,t){e.state.delayingBlurEvent&&(e.state.delayingBlurEvent=!1),"nocursor"!=e.options.readOnly&&(e.state.focused||(ge(e,"focus",e,t),e.state.focused=!0,H(e.display.wrapper,"CodeMirror-focused"),e.curOp||e.display.selForContextMenu==e.doc.sel||(e.display.input.reset(),a&&setTimeout(function(){return e.display.input.reset(!0)},20)),e.display.input.receivedFocus()),bn(e))}function Sn(e,t){e.state.delayingBlurEvent||(e.state.focused&&(ge(e,"blur",e,t),e.state.focused=!1,T(e.display.wrapper,"CodeMirror-focused")),clearInterval(e.display.blinker),setTimeout(function(){e.state.focused||(e.display.shift=!1)},150))}function Ln(e){for(var t=e.display,r=t.lineDiv.offsetTop,n=0;n.005||f<-.005)&&($e(i.line,a),kn(i.line),i.rest))for(var d=0;de.display.sizerWidth){var p=Math.ceil(u/tn(e.display));p>e.display.maxLineLength&&(e.display.maxLineLength=p,e.display.maxLine=i.line,e.display.maxLineChanged=!0)}}}}function kn(e){if(e.widgets)for(var t=0;t=l&&(o=Ze(t,Vt(Xe(t,a))-e.wrapper.clientHeight),l=a)}return{from:o,to:Math.max(l,o+1)}}function Mn(e,t){var r=e.display,n=en(e.display);t.top<0&&(t.top=0);var i=e.curOp&&null!=e.curOp.scrollTop?e.curOp.scrollTop:r.scroller.scrollTop,o=Mr(e),l={};t.bottom-t.top>o&&(t.bottom=t.top+o);var s=e.doc.height+Sr(r),a=t.tops-n;if(t.topi+o){var c=Math.min(t.top,(u?s:t.bottom)-o);c!=i&&(l.scrollTop=c)}var h=e.curOp&&null!=e.curOp.scrollLeft?e.curOp.scrollLeft:r.scroller.scrollLeft,f=Tr(e)-(e.options.fixedGutter?r.gutters.offsetWidth:0),d=t.right-t.left>f;return d&&(t.right=t.left+f),t.left<10?l.scrollLeft=0:t.leftf+h-3&&(l.scrollLeft=t.right+(d?0:10)-f),l}function Nn(e,t){null!=t&&(Dn(e),e.curOp.scrollTop=(null==e.curOp.scrollTop?e.doc.scrollTop:e.curOp.scrollTop)+t)}function On(e){Dn(e);var t=e.getCursor();e.curOp.scrollToPos={from:t,to:t,margin:e.options.cursorScrollMargin}}function An(e,t,r){null==t&&null==r||Dn(e),null!=t&&(e.curOp.scrollLeft=t),null!=r&&(e.curOp.scrollTop=r)}function Dn(e){var t=e.curOp.scrollToPos;t&&(e.curOp.scrollToPos=null,Wn(e,Yr(e,t.from),Yr(e,t.to),t.margin))}function Wn(e,t,r,n){var i=Mn(e,{left:Math.min(t.left,r.left),top:Math.min(t.top,r.top)-n,right:Math.max(t.right,r.right),bottom:Math.max(t.bottom,r.bottom)+n});An(e,i.scrollLeft,i.scrollTop)}function Hn(e,t){Math.abs(e.doc.scrollTop-t)<2||(r||oi(e,{top:t}),Fn(e,t,!0),r&&oi(e),ei(e,100))}function Fn(e,t,r){t=Math.min(e.display.scroller.scrollHeight-e.display.scroller.clientHeight,t),(e.display.scroller.scrollTop!=t||r)&&(e.doc.scrollTop=t,e.display.scrollbars.setScrollTop(t),e.display.scroller.scrollTop!=t&&(e.display.scroller.scrollTop=t))}function Pn(e,t,r,n){t=Math.min(t,e.display.scroller.scrollWidth-e.display.scroller.clientWidth),(r?t==e.doc.scrollLeft:Math.abs(e.doc.scrollLeft-t)<2)&&!n||(e.doc.scrollLeft=t,ai(e),e.display.scroller.scrollLeft!=t&&(e.display.scroller.scrollLeft=t),e.display.scrollbars.setScrollLeft(t))}function En(e){var t=e.display,r=t.gutters.offsetWidth,n=Math.round(e.doc.height+Sr(e.display));return{clientHeight:t.scroller.clientHeight,viewHeight:t.wrapper.clientHeight,scrollWidth:t.scroller.scrollWidth,clientWidth:t.scroller.clientWidth,viewWidth:t.wrapper.clientWidth,barLeft:e.options.fixedGutter?r:0,docHeight:n,scrollHeight:n+kr(e)+t.barHeight,nativeBarWidth:t.nativeBarWidth,gutterWidth:r}}var In=function(e,t,r){this.cm=r;var n=this.vert=O("div",[O("div",null,null,"min-width: 1px")],"CodeMirror-vscrollbar"),i=this.horiz=O("div",[O("div",null,null,"height: 100%; min-height: 1px")],"CodeMirror-hscrollbar");n.tabIndex=i.tabIndex=-1,e(n),e(i),fe(n,"scroll",function(){n.clientHeight&&t(n.scrollTop,"vertical")}),fe(i,"scroll",function(){i.clientWidth&&t(i.scrollLeft,"horizontal")}),this.checkedZeroWidth=!1,l&&s<8&&(this.horiz.style.minHeight=this.vert.style.minWidth="18px")};In.prototype.update=function(e){var t=e.scrollWidth>e.clientWidth+1,r=e.scrollHeight>e.clientHeight+1,n=e.nativeBarWidth;if(r){this.vert.style.display="block",this.vert.style.bottom=t?n+"px":"0";var i=e.viewHeight-(t?n:0);this.vert.firstChild.style.height=Math.max(0,e.scrollHeight-e.clientHeight+i)+"px"}else this.vert.style.display="",this.vert.firstChild.style.height="0";if(t){this.horiz.style.display="block",this.horiz.style.right=r?n+"px":"0",this.horiz.style.left=e.barLeft+"px";var o=e.viewWidth-e.barLeft-(r?n:0);this.horiz.firstChild.style.width=Math.max(0,e.scrollWidth-e.clientWidth+o)+"px"}else this.horiz.style.display="",this.horiz.firstChild.style.width="0";return!this.checkedZeroWidth&&e.clientHeight>0&&(0==n&&this.zeroWidthHack(),this.checkedZeroWidth=!0),{right:r?n:0,bottom:t?n:0}},In.prototype.setScrollLeft=function(e){this.horiz.scrollLeft!=e&&(this.horiz.scrollLeft=e),this.disableHoriz&&this.enableZeroWidthBar(this.horiz,this.disableHoriz,"horiz")},In.prototype.setScrollTop=function(e){this.vert.scrollTop!=e&&(this.vert.scrollTop=e),this.disableVert&&this.enableZeroWidthBar(this.vert,this.disableVert,"vert")},In.prototype.zeroWidthHack=function(){var e=y&&!d?"12px":"18px";this.horiz.style.height=this.vert.style.width=e,this.horiz.style.pointerEvents=this.vert.style.pointerEvents="none",this.disableHoriz=new R,this.disableVert=new R},In.prototype.enableZeroWidthBar=function(e,t,r){e.style.pointerEvents="auto",t.set(1e3,function n(){var i=e.getBoundingClientRect();("vert"==r?document.elementFromPoint(i.right-1,(i.top+i.bottom)/2):document.elementFromPoint((i.right+i.left)/2,i.bottom-1))!=e?e.style.pointerEvents="none":t.set(1e3,n)})},In.prototype.clear=function(){var e=this.horiz.parentNode;e.removeChild(this.horiz),e.removeChild(this.vert)};var zn=function(){};function Rn(e,t){t||(t=En(e));var r=e.display.barWidth,n=e.display.barHeight;Bn(e,t);for(var i=0;i<4&&r!=e.display.barWidth||n!=e.display.barHeight;i++)r!=e.display.barWidth&&e.options.lineWrapping&&Ln(e),Bn(e,En(e)),r=e.display.barWidth,n=e.display.barHeight}function Bn(e,t){var r=e.display,n=r.scrollbars.update(t);r.sizer.style.paddingRight=(r.barWidth=n.right)+"px",r.sizer.style.paddingBottom=(r.barHeight=n.bottom)+"px",r.heightForcer.style.borderBottom=n.bottom+"px solid transparent",n.right&&n.bottom?(r.scrollbarFiller.style.display="block",r.scrollbarFiller.style.height=n.bottom+"px",r.scrollbarFiller.style.width=n.right+"px"):r.scrollbarFiller.style.display="",n.bottom&&e.options.coverGutterNextToScrollbar&&e.options.fixedGutter?(r.gutterFiller.style.display="block",r.gutterFiller.style.height=n.bottom+"px",r.gutterFiller.style.width=t.gutterWidth+"px"):r.gutterFiller.style.display=""}zn.prototype.update=function(){return{bottom:0,right:0}},zn.prototype.setScrollLeft=function(){},zn.prototype.setScrollTop=function(){},zn.prototype.clear=function(){};var Gn={native:In,null:zn};function Un(e){e.display.scrollbars&&(e.display.scrollbars.clear(),e.display.scrollbars.addClass&&T(e.display.wrapper,e.display.scrollbars.addClass)),e.display.scrollbars=new Gn[e.options.scrollbarStyle](function(t){e.display.wrapper.insertBefore(t,e.display.scrollbarFiller),fe(t,"mousedown",function(){e.state.focused&&setTimeout(function(){return e.display.input.focus()},0)}),t.setAttribute("cm-not-content","true")},function(t,r){"horizontal"==r?Pn(e,t):Hn(e,t)},e),e.display.scrollbars.addClass&&H(e.display.wrapper,e.display.scrollbars.addClass)}var Vn=0;function Kn(e){var t;e.curOp={cm:e,viewChanged:!1,startHeight:e.doc.height,forceUpdate:!1,updateInput:0,typing:!1,changeObjs:null,cursorActivityHandlers:null,cursorActivityCalled:0,selectionChanged:!1,updateMaxLine:!1,scrollLeft:null,scrollTop:null,scrollToPos:null,focus:!1,id:++Vn},t=e.curOp,or?or.ops.push(t):t.ownsGroup=or={ops:[t],delayedCallbacks:[]}}function jn(e){var t=e.curOp;t&&function(e,t){var r=e.ownsGroup;if(r)try{!function(e){var t=e.delayedCallbacks,r=0;do{for(;r=r.viewTo)||r.maxLineChanged&&t.options.lineWrapping,e.update=e.mustUpdate&&new ri(t,e.mustUpdate&&{top:e.scrollTop,ensure:e.scrollToPos},e.forceUpdate)}function Yn(e){var t=e.cm,r=t.display;e.updatedDisplay&&Ln(t),e.barMeasure=En(t),r.maxLineChanged&&!t.options.lineWrapping&&(e.adjustWidthTo=Or(t,r.maxLine,r.maxLine.text.length).left+3,t.display.sizerWidth=e.adjustWidthTo,e.barMeasure.scrollWidth=Math.max(r.scroller.clientWidth,r.sizer.offsetLeft+e.adjustWidthTo+kr(t)+t.display.barWidth),e.maxScrollLeft=Math.max(0,r.sizer.offsetLeft+e.adjustWidthTo-Tr(t))),(e.updatedDisplay||e.selectionChanged)&&(e.preparedSelection=r.input.prepareSelection())}function _n(e){var t=e.cm;null!=e.adjustWidthTo&&(t.display.sizer.style.minWidth=e.adjustWidthTo+"px",e.maxScrollLeft(window.innerHeight||document.documentElement.clientHeight)&&(i=!1),null!=i&&!p){var o=O("div","​",null,"position: absolute;\n top: "+(t.top-r.viewOffset-Cr(e.display))+"px;\n height: "+(t.bottom-t.top+kr(e)+r.barHeight)+"px;\n left: "+t.left+"px; width: "+Math.max(2,t.right-t.left)+"px;");e.display.lineSpace.appendChild(o),o.scrollIntoView(i),e.display.lineSpace.removeChild(o)}}}(t,function(e,t,r,n){var i;null==n&&(n=0),e.options.lineWrapping||t!=r||(r="before"==(t=t.ch?et(t.line,"before"==t.sticky?t.ch-1:t.ch,"after"):t).sticky?et(t.line,t.ch+1,"before"):t);for(var o=0;o<5;o++){var l=!1,s=Xr(e,t),a=r&&r!=t?Xr(e,r):s,u=Mn(e,i={left:Math.min(s.left,a.left),top:Math.min(s.top,a.top)-n,right:Math.max(s.left,a.left),bottom:Math.max(s.bottom,a.bottom)+n}),c=e.doc.scrollTop,h=e.doc.scrollLeft;if(null!=u.scrollTop&&(Hn(e,u.scrollTop),Math.abs(e.doc.scrollTop-c)>1&&(l=!0)),null!=u.scrollLeft&&(Pn(e,u.scrollLeft),Math.abs(e.doc.scrollLeft-h)>1&&(l=!0)),!l)break}return i}(t,st(n,e.scrollToPos.from),st(n,e.scrollToPos.to),e.scrollToPos.margin));var i=e.maybeHiddenMarkers,o=e.maybeUnhiddenMarkers;if(i)for(var l=0;l=e.display.viewTo)){var r=+new Date+e.options.workTime,n=dt(e,t.highlightFrontier),i=[];t.iter(n.line,Math.min(t.first+t.size,e.display.viewTo+500),function(o){if(n.line>=e.display.viewFrom){var l=o.styles,s=o.text.length>e.options.maxHighlightLength?Ue(t.mode,n.state):null,a=ht(e,o,n,!0);s&&(n.state=s),o.styles=a.styles;var u=o.styleClasses,c=a.classes;c?o.styleClasses=c:u&&(o.styleClasses=null);for(var h=!l||l.length!=o.styles.length||u!=c&&(!u||!c||u.bgClass!=c.bgClass||u.textClass!=c.textClass),f=0;!h&&fr)return ei(e,e.options.workDelay),!0}),t.highlightFrontier=n.line,t.modeFrontier=Math.max(t.modeFrontier,n.line),i.length&&qn(e,function(){for(var t=0;t=r.viewFrom&&t.visible.to<=r.viewTo&&(null==r.updateLineNumbers||r.updateLineNumbers>=r.viewTo)&&r.renderedView==r.view&&0==dn(e))return!1;ui(e)&&(hn(e),t.dims=rn(e));var i=n.first+n.size,o=Math.max(t.visible.from-e.options.viewportMargin,n.first),l=Math.min(i,t.visible.to+e.options.viewportMargin);r.viewFroml&&r.viewTo-l<20&&(l=Math.min(i,r.viewTo)),Ct&&(o=Rt(e.doc,o),l=Bt(e.doc,l));var s=o!=r.viewFrom||l!=r.viewTo||r.lastWrapHeight!=t.wrapperHeight||r.lastWrapWidth!=t.wrapperWidth;!function(e,t,r){var n=e.display;0==n.view.length||t>=n.viewTo||r<=n.viewFrom?(n.view=ir(e,t,r),n.viewFrom=t):(n.viewFrom>t?n.view=ir(e,t,n.viewFrom).concat(n.view):n.viewFromr&&(n.view=n.view.slice(0,an(e,r)))),n.viewTo=r}(e,o,l),r.viewOffset=Vt(Xe(e.doc,r.viewFrom)),e.display.mover.style.top=r.viewOffset+"px";var u=dn(e);if(!s&&0==u&&!t.force&&r.renderedView==r.view&&(null==r.updateLineNumbers||r.updateLineNumbers>=r.viewTo))return!1;var c=function(e){if(e.hasFocus())return null;var t=W();if(!t||!D(e.display.lineDiv,t))return null;var r={activeElt:t};if(window.getSelection){var n=window.getSelection();n.anchorNode&&n.extend&&D(e.display.lineDiv,n.anchorNode)&&(r.anchorNode=n.anchorNode,r.anchorOffset=n.anchorOffset,r.focusNode=n.focusNode,r.focusOffset=n.focusOffset)}return r}(e);return u>4&&(r.lineDiv.style.display="none"),function(e,t,r){var n=e.display,i=e.options.lineNumbers,o=n.lineDiv,l=o.firstChild;function s(t){var r=t.nextSibling;return a&&y&&e.display.currentWheelTarget==t?t.style.display="none":t.parentNode.removeChild(t),r}for(var u=n.view,c=n.viewFrom,h=0;h-1&&(d=!1),ur(e,f,c,r)),d&&(M(f.lineNumber),f.lineNumber.appendChild(document.createTextNode(Je(e.options,c)))),l=f.node.nextSibling}else{var p=vr(e,f,c,r);o.insertBefore(p,l)}c+=f.size}for(;l;)l=s(l)}(e,r.updateLineNumbers,t.dims),u>4&&(r.lineDiv.style.display=""),r.renderedView=r.view,function(e){if(e&&e.activeElt&&e.activeElt!=W()&&(e.activeElt.focus(),e.anchorNode&&D(document.body,e.anchorNode)&&D(document.body,e.focusNode))){var t=window.getSelection(),r=document.createRange();r.setEnd(e.anchorNode,e.anchorOffset),r.collapse(!1),t.removeAllRanges(),t.addRange(r),t.extend(e.focusNode,e.focusOffset)}}(c),M(r.cursorDiv),M(r.selectionDiv),r.gutters.style.height=r.sizer.style.minHeight=0,s&&(r.lastWrapHeight=t.wrapperHeight,r.lastWrapWidth=t.wrapperWidth,ei(e,400)),r.updateLineNumbers=null,!0}function ii(e,t){for(var r=t.viewport,n=!0;(n&&e.options.lineWrapping&&t.oldDisplayWidth!=Tr(e)||(r&&null!=r.top&&(r={top:Math.min(e.doc.height+Sr(e.display)-Mr(e),r.top)}),t.visible=Tn(e.display,e.doc,r),!(t.visible.from>=e.display.viewFrom&&t.visible.to<=e.display.viewTo)))&&ni(e,t);n=!1){Ln(e);var i=En(e);pn(e),Rn(e,i),si(e,i),t.force=!1}t.signal(e,"update",e),e.display.viewFrom==e.display.reportedViewFrom&&e.display.viewTo==e.display.reportedViewTo||(t.signal(e,"viewportChange",e,e.display.viewFrom,e.display.viewTo),e.display.reportedViewFrom=e.display.viewFrom,e.display.reportedViewTo=e.display.viewTo)}function oi(e,t){var r=new ri(e,t);if(ni(e,r)){Ln(e),ii(e,r);var n=En(e);pn(e),Rn(e,n),si(e,n),r.finish()}}function li(e){var t=e.gutters.offsetWidth;e.sizer.style.marginLeft=t+"px"}function si(e,t){e.display.sizer.style.minHeight=t.docHeight+"px",e.display.heightForcer.style.top=t.docHeight+"px",e.display.gutters.style.height=t.docHeight+e.display.barHeight+kr(e)+"px"}function ai(e){var t=e.display,r=t.view;if(t.alignWidgets||t.gutters.firstChild&&e.options.fixedGutter){for(var n=nn(t)-t.scroller.scrollLeft+e.doc.scrollLeft,i=t.gutters.offsetWidth,o=n+"px",l=0;ls.clientWidth,c=s.scrollHeight>s.clientHeight;if(i&&u||o&&c){if(o&&y&&a)e:for(var f=t.target,d=l.view;f!=s;f=f.parentNode)for(var p=0;p=0&&tt(e,n.to())<=0)return r}return-1};var bi=function(e,t){this.anchor=e,this.head=t};function wi(e,t,r){var n=e&&e.options.selectionsMayTouch,i=t[r];t.sort(function(e,t){return tt(e.from(),t.from())}),r=B(t,i);for(var o=1;o0:a>=0){var u=ot(s.from(),l.from()),c=it(s.to(),l.to()),h=s.empty()?l.from()==l.head:s.from()==s.head;o<=r&&--r,t.splice(--o,2,new bi(h?c:u,h?u:c))}}return new yi(t,r)}function xi(e,t){return new yi([new bi(e,t||e)],0)}function Ci(e){return e.text?et(e.from.line+e.text.length-1,$(e.text).length+(1==e.text.length?e.from.ch:0)):e.to}function Si(e,t){if(tt(e,t.from)<0)return e;if(tt(e,t.to)<=0)return Ci(t);var r=e.line+t.text.length-(t.to.line-t.from.line)-1,n=e.ch;return e.line==t.to.line&&(n+=Ci(t).ch-t.to.ch),et(r,n)}function Li(e,t){for(var r=[],n=0;n1&&e.remove(s.line+1,p-1),e.insert(s.line+1,m)}sr(e,"change",e,t)}function Ai(e,t,r){!function e(n,i,o){if(n.linked)for(var l=0;ls-(e.cm?e.cm.options.historyEventDelay:500)||"*"==t.origin.charAt(0)))&&(o=function(e,t){return t?(Pi(e.done),$(e.done)):e.done.length&&!$(e.done).ranges?$(e.done):e.done.length>1&&!e.done[e.done.length-2].ranges?(e.done.pop(),$(e.done)):void 0}(i,i.lastOp==n)))l=$(o.changes),0==tt(t.from,t.to)&&0==tt(t.from,l.to)?l.to=Ci(t):o.changes.push(Fi(e,t));else{var a=$(i.done);for(a&&a.ranges||zi(e.sel,i.done),o={changes:[Fi(e,t)],generation:i.generation},i.done.push(o);i.done.length>i.undoDepth;)i.done.shift(),i.done[0].ranges||i.done.shift()}i.done.push(r),i.generation=++i.maxGeneration,i.lastModTime=i.lastSelTime=s,i.lastOp=i.lastSelOp=n,i.lastOrigin=i.lastSelOrigin=t.origin,l||ge(e,"historyAdded")}function Ii(e,t,r,n){var i=e.history,o=n&&n.origin;r==i.lastSelOp||o&&i.lastSelOrigin==o&&(i.lastModTime==i.lastSelTime&&i.lastOrigin==o||function(e,t,r,n){var i=t.charAt(0);return"*"==i||"+"==i&&r.ranges.length==n.ranges.length&&r.somethingSelected()==n.somethingSelected()&&new Date-e.history.lastSelTime<=(e.cm?e.cm.options.historyEventDelay:500)}(e,o,$(i.done),t))?i.done[i.done.length-1]=t:zi(t,i.done),i.lastSelTime=+new Date,i.lastSelOrigin=o,i.lastSelOp=r,n&&!1!==n.clearRedo&&Pi(i.undone)}function zi(e,t){var r=$(t);r&&r.ranges&&r.equals(e)||t.push(e)}function Ri(e,t,r,n){var i=t["spans_"+e.id],o=0;e.iter(Math.max(e.first,r),Math.min(e.first+e.size,n),function(r){r.markedSpans&&((i||(i=t["spans_"+e.id]={}))[o]=r.markedSpans),++o})}function Bi(e){if(!e)return null;for(var t,r=0;r-1&&($(s)[h]=u[h],delete u[h])}}}return n}function Vi(e,t,r,n){if(n){var i=e.anchor;if(r){var o=tt(t,i)<0;o!=tt(r,i)<0?(i=t,t=r):o!=tt(t,r)<0&&(t=r)}return new bi(i,t)}return new bi(r||t,t)}function Ki(e,t,r,n,i){null==i&&(i=e.cm&&(e.cm.display.shift||e.extend)),$i(e,new yi([Vi(e.sel.primary(),t,r,i)],0),n)}function ji(e,t,r){for(var n=[],i=e.cm&&(e.cm.display.shift||e.extend),o=0;o=t.ch:s.to>t.ch))){if(i&&(ge(a,"beforeCursorEnter"),a.explicitlyCleared)){if(o.markedSpans){--l;continue}break}if(!a.atomic)continue;if(r){var h=a.find(n<0?1:-1),f=void 0;if((n<0?c:u)&&(h=ro(e,h,-n,h&&h.line==t.line?o:null)),h&&h.line==t.line&&(f=tt(h,r))&&(n<0?f<0:f>0))return eo(e,h,t,n,i)}var d=a.find(n<0?-1:1);return(n<0?u:c)&&(d=ro(e,d,n,d.line==t.line?o:null)),d?eo(e,d,t,n,i):null}}return t}function to(e,t,r,n,i){var o=n||1,l=eo(e,t,r,o,i)||!i&&eo(e,t,r,o,!0)||eo(e,t,r,-o,i)||!i&&eo(e,t,r,-o,!0);return l||(e.cantEdit=!0,et(e.first,0))}function ro(e,t,r,n){return r<0&&0==t.ch?t.line>e.first?st(e,et(t.line-1)):null:r>0&&t.ch==(n||Xe(e,t.line)).text.length?t.line0)){var c=[a,1],h=tt(u.from,s.from),f=tt(u.to,s.to);(h<0||!l.inclusiveLeft&&!h)&&c.push({from:u.from,to:s.from}),(f>0||!l.inclusiveRight&&!f)&&c.push({from:s.to,to:u.to}),i.splice.apply(i,c),a+=c.length-3}}return i}(e,t.from,t.to);if(n)for(var i=n.length-1;i>=0;--i)lo(e,{from:n[i].from,to:n[i].to,text:i?[""]:t.text,origin:t.origin});else lo(e,t)}}function lo(e,t){if(1!=t.text.length||""!=t.text[0]||0!=tt(t.from,t.to)){var r=Li(e,t);Ei(e,t,r,e.cm?e.cm.curOp.id:NaN),uo(e,t,r,Tt(e,t));var n=[];Ai(e,function(e,r){r||-1!=B(n,e.history)||(po(e.history,t),n.push(e.history)),uo(e,t,null,Tt(e,t))})}}function so(e,t,r){var n=e.cm&&e.cm.state.suppressEdits;if(!n||r){for(var i,o=e.history,l=e.sel,s="undo"==t?o.done:o.undone,a="undo"==t?o.undone:o.done,u=0;u=0;--d){var p=f(d);if(p)return p.v}}}}function ao(e,t){if(0!=t&&(e.first+=t,e.sel=new yi(q(e.sel.ranges,function(e){return new bi(et(e.anchor.line+t,e.anchor.ch),et(e.head.line+t,e.head.ch))}),e.sel.primIndex),e.cm)){un(e.cm,e.first,e.first-t,t);for(var r=e.cm.display,n=r.viewFrom;ne.lastLine())){if(t.from.lineo&&(t={from:t.from,to:et(o,Xe(e,o).text.length),text:[t.text[0]],origin:t.origin}),t.removed=Ye(e,t.from,t.to),r||(r=Li(e,t)),e.cm?function(e,t,r){var n=e.doc,i=e.display,o=t.from,l=t.to,s=!1,a=o.line;e.options.lineWrapping||(a=qe(zt(Xe(n,o.line))),n.iter(a,l.line+1,function(e){if(e==i.maxLine)return s=!0,!0}));n.sel.contains(t.from,t.to)>-1&&me(e);Oi(n,t,r,on(e)),e.options.lineWrapping||(n.iter(a,o.line+t.text.length,function(e){var t=Kt(e);t>i.maxLineLength&&(i.maxLine=e,i.maxLineLength=t,i.maxLineChanged=!0,s=!1)}),s&&(e.curOp.updateMaxLine=!0));(function(e,t){if(e.modeFrontier=Math.min(e.modeFrontier,t),!(e.highlightFrontierr;n--){var i=Xe(e,n).stateAfter;if(i&&(!(i instanceof ut)||n+i.lookAhead1||!(this.children[0]instanceof vo))){var s=[];this.collapse(s),this.children=[new vo(s)],this.children[0].parent=this}},collapse:function(e){for(var t=0;t50){for(var l=i.lines.length%25+25,s=l;s10);e.parent.maybeSpill()}},iterN:function(e,t,r){for(var n=0;n0||0==l&&!1!==o.clearWhenEmpty)return o;if(o.replacedWith&&(o.collapsed=!0,o.widgetNode=A("span",[o.replacedWith],"CodeMirror-widget"),n.handleMouseEvents||o.widgetNode.setAttribute("cm-ignore-events","true"),n.insertLeft&&(o.widgetNode.insertLeft=!0)),o.collapsed){if(It(e,t.line,t,r,o)||t.line!=r.line&&It(e,r.line,t,r,o))throw new Error("Inserting collapsed marker partially overlapping an existing one");Ct=!0}o.addToHistory&&Ei(e,{from:t,to:r,origin:"markText"},e.sel,NaN);var s,a=t.line,u=e.cm;if(e.iter(a,r.line+1,function(e){u&&o.collapsed&&!u.options.lineWrapping&&zt(e)==u.display.maxLine&&(s=!0),o.collapsed&&a!=t.line&&$e(e,0),function(e,t){e.markedSpans=e.markedSpans?e.markedSpans.concat([t]):[t],t.marker.attachLine(e)}(e,new St(o,a==t.line?t.ch:null,a==r.line?r.ch:null)),++a}),o.collapsed&&e.iter(t.line,r.line+1,function(t){Gt(e,t)&&$e(t,0)}),o.clearOnEnter&&fe(o,"beforeCursorEnter",function(){return o.clear()}),o.readOnly&&(xt=!0,(e.history.done.length||e.history.undone.length)&&e.clearHistory()),o.collapsed&&(o.id=++wo,o.atomic=!0),u){if(s&&(u.curOp.updateMaxLine=!0),o.collapsed)un(u,t.line,r.line+1);else if(o.className||o.startStyle||o.endStyle||o.css||o.attributes||o.title)for(var c=t.line;c<=r.line;c++)cn(u,c,"text");o.atomic&&Qi(u.doc),sr(u,"markerAdded",u,o)}return o}xo.prototype.clear=function(){if(!this.explicitlyCleared){var e=this.doc.cm,t=e&&!e.curOp;if(t&&Kn(e),ye(this,"clear")){var r=this.find();r&&sr(this,"clear",r.from,r.to)}for(var n=null,i=null,o=0;oe.display.maxLineLength&&(e.display.maxLine=u,e.display.maxLineLength=c,e.display.maxLineChanged=!0)}null!=n&&e&&this.collapsed&&un(e,n,i+1),this.lines.length=0,this.explicitlyCleared=!0,this.atomic&&this.doc.cantEdit&&(this.doc.cantEdit=!1,e&&Qi(e.doc)),e&&sr(e,"markerCleared",e,this,n,i),t&&jn(e),this.parent&&this.parent.clear()}},xo.prototype.find=function(e,t){var r,n;null==e&&"bookmark"==this.type&&(e=1);for(var i=0;i=0;a--)oo(this,n[a]);s?_i(this,s):this.cm&&On(this.cm)}),undo:Jn(function(){so(this,"undo")}),redo:Jn(function(){so(this,"redo")}),undoSelection:Jn(function(){so(this,"undo",!0)}),redoSelection:Jn(function(){so(this,"redo",!0)}),setExtending:function(e){this.extend=e},getExtending:function(){return this.extend},historySize:function(){for(var e=this.history,t=0,r=0,n=0;n=e.ch)&&t.push(i.marker.parent||i.marker)}return t},findMarks:function(e,t,r){e=st(this,e),t=st(this,t);var n=[],i=e.line;return this.iter(e.line,t.line+1,function(o){var l=o.markedSpans;if(l)for(var s=0;s=a.to||null==a.from&&i!=e.line||null!=a.from&&i==t.line&&a.from>=t.ch||r&&!r(a.marker)||n.push(a.marker.parent||a.marker)}++i}),n},getAllMarks:function(){var e=[];return this.iter(function(t){var r=t.markedSpans;if(r)for(var n=0;ne)return t=e,!0;e-=o,++r}),st(this,et(r,t))},indexFromPos:function(e){var t=(e=st(this,e)).ch;if(e.linet&&(t=e.from),null!=e.to&&e.to-1)return t.state.draggingText(e),void setTimeout(function(){return t.display.input.focus()},20);try{var c=e.dataTransfer.getData("Text");if(c){var h;if(t.state.draggingText&&!t.state.draggingText.copy&&(h=t.listSelections()),qi(t.doc,xi(r,r)),h)for(var f=0;f=0;t--)co(e.doc,"",n[t].from,n[t].to,"+delete");On(e)})}function _o(e,t,r){var n=oe(e.text,t+r,r);return n<0||n>e.text.length?null:n}function $o(e,t,r){var n=_o(e,t.ch,r);return null==n?null:new et(t.line,n,r<0?"after":"before")}function qo(e,t,r,n,i){if(e){var o=ce(r,t.doc.direction);if(o){var l,s=i<0?$(o):o[0],a=i<0==(1==s.level)?"after":"before";if(s.level>0||"rtl"==t.doc.direction){var u=Dr(t,r);l=i<0?r.text.length-1:0;var c=Wr(t,u,l).top;l=le(function(e){return Wr(t,u,e).top==c},i<0==(1==s.level)?s.from:s.to-1,l),"before"==a&&(l=_o(r,l,1))}else l=i<0?s.to:s.from;return new et(n,l,a)}}return new et(n,i<0?r.text.length:0,i<0?"before":"after")}Ro.basic={Left:"goCharLeft",Right:"goCharRight",Up:"goLineUp",Down:"goLineDown",End:"goLineEnd",Home:"goLineStartSmart",PageUp:"goPageUp",PageDown:"goPageDown",Delete:"delCharAfter",Backspace:"delCharBefore","Shift-Backspace":"delCharBefore",Tab:"defaultTab","Shift-Tab":"indentAuto",Enter:"newlineAndIndent",Insert:"toggleOverwrite",Esc:"singleSelection"},Ro.pcDefault={"Ctrl-A":"selectAll","Ctrl-D":"deleteLine","Ctrl-Z":"undo","Shift-Ctrl-Z":"redo","Ctrl-Y":"redo","Ctrl-Home":"goDocStart","Ctrl-End":"goDocEnd","Ctrl-Up":"goLineUp","Ctrl-Down":"goLineDown","Ctrl-Left":"goGroupLeft","Ctrl-Right":"goGroupRight","Alt-Left":"goLineStart","Alt-Right":"goLineEnd","Ctrl-Backspace":"delGroupBefore","Ctrl-Delete":"delGroupAfter","Ctrl-S":"save","Ctrl-F":"find","Ctrl-G":"findNext","Shift-Ctrl-G":"findPrev","Shift-Ctrl-F":"replace","Shift-Ctrl-R":"replaceAll","Ctrl-[":"indentLess","Ctrl-]":"indentMore","Ctrl-U":"undoSelection","Shift-Ctrl-U":"redoSelection","Alt-U":"redoSelection",fallthrough:"basic"},Ro.emacsy={"Ctrl-F":"goCharRight","Ctrl-B":"goCharLeft","Ctrl-P":"goLineUp","Ctrl-N":"goLineDown","Alt-F":"goWordRight","Alt-B":"goWordLeft","Ctrl-A":"goLineStart","Ctrl-E":"goLineEnd","Ctrl-V":"goPageDown","Shift-Ctrl-V":"goPageUp","Ctrl-D":"delCharAfter","Ctrl-H":"delCharBefore","Alt-D":"delWordAfter","Alt-Backspace":"delWordBefore","Ctrl-K":"killLine","Ctrl-T":"transposeChars","Ctrl-O":"openLine"},Ro.macDefault={"Cmd-A":"selectAll","Cmd-D":"deleteLine","Cmd-Z":"undo","Shift-Cmd-Z":"redo","Cmd-Y":"redo","Cmd-Home":"goDocStart","Cmd-Up":"goDocStart","Cmd-End":"goDocEnd","Cmd-Down":"goDocEnd","Alt-Left":"goGroupLeft","Alt-Right":"goGroupRight","Cmd-Left":"goLineLeft","Cmd-Right":"goLineRight","Alt-Backspace":"delGroupBefore","Ctrl-Alt-Backspace":"delGroupAfter","Alt-Delete":"delGroupAfter","Cmd-S":"save","Cmd-F":"find","Cmd-G":"findNext","Shift-Cmd-G":"findPrev","Cmd-Alt-F":"replace","Shift-Cmd-Alt-F":"replaceAll","Cmd-[":"indentLess","Cmd-]":"indentMore","Cmd-Backspace":"delWrappedLineLeft","Cmd-Delete":"delWrappedLineRight","Cmd-U":"undoSelection","Shift-Cmd-U":"redoSelection","Ctrl-Up":"goDocStart","Ctrl-Down":"goDocEnd",fallthrough:["basic","emacsy"]},Ro.default=y?Ro.macDefault:Ro.pcDefault;var Zo={selectAll:no,singleSelection:function(e){return e.setSelection(e.getCursor("anchor"),e.getCursor("head"),V)},killLine:function(e){return Yo(e,function(t){if(t.empty()){var r=Xe(e.doc,t.head.line).text.length;return t.head.ch==r&&t.head.line0)i=new et(i.line,i.ch+1),e.replaceRange(o.charAt(i.ch-1)+o.charAt(i.ch-2),et(i.line,i.ch-2),i,"+transpose");else if(i.line>e.doc.first){var l=Xe(e.doc,i.line-1).text;l&&(i=new et(i.line,1),e.replaceRange(o.charAt(0)+e.doc.lineSeparator()+l.charAt(l.length-1),et(i.line-1,l.length-1),i,"+transpose"))}r.push(new bi(i,i))}e.setSelections(r)})},newlineAndIndent:function(e){return qn(e,function(){for(var t=e.listSelections(),r=t.length-1;r>=0;r--)e.replaceRange(e.doc.lineSeparator(),t[r].anchor,t[r].head,"+input");t=e.listSelections();for(var n=0;n-1&&(tt((i=u.ranges[i]).from(),t)<0||t.xRel>0)&&(tt(i.to(),t)>0||t.xRel<0)?function(e,t,r,n){var i=e.display,o=!1,u=Zn(e,function(t){a&&(i.scroller.draggable=!1),e.state.draggingText=!1,pe(i.wrapper.ownerDocument,"mouseup",u),pe(i.wrapper.ownerDocument,"mousemove",c),pe(i.scroller,"dragstart",h),pe(i.scroller,"drop",u),o||(we(t),n.addNew||Ki(e.doc,r,null,null,n.extend),a||l&&9==s?setTimeout(function(){i.wrapper.ownerDocument.body.focus(),i.input.focus()},20):i.input.focus())}),c=function(e){o=o||Math.abs(t.clientX-e.clientX)+Math.abs(t.clientY-e.clientY)>=10},h=function(){return o=!0};a&&(i.scroller.draggable=!0);e.state.draggingText=u,u.copy=!n.moveOnDrag,i.scroller.dragDrop&&i.scroller.dragDrop();fe(i.wrapper.ownerDocument,"mouseup",u),fe(i.wrapper.ownerDocument,"mousemove",c),fe(i.scroller,"dragstart",h),fe(i.scroller,"drop",u),xn(e),setTimeout(function(){return i.input.focus()},20)}(e,n,t,o):function(e,t,r,n){var i=e.display,o=e.doc;we(t);var l,s,a=o.sel,u=a.ranges;n.addNew&&!n.extend?(s=o.sel.contains(r),l=s>-1?u[s]:new bi(r,r)):(l=o.sel.primary(),s=o.sel.primIndex);if("rectangle"==n.unit)n.addNew||(l=new bi(r,r)),r=sn(e,t,!0,!0),s=-1;else{var c=dl(e,r,n.unit);l=n.extend?Vi(l,c.anchor,c.head,n.extend):c}n.addNew?-1==s?(s=u.length,$i(o,wi(e,u.concat([l]),s),{scroll:!1,origin:"*mouse"})):u.length>1&&u[s].empty()&&"char"==n.unit&&!n.extend?($i(o,wi(e,u.slice(0,s).concat(u.slice(s+1)),0),{scroll:!1,origin:"*mouse"}),a=o.sel):Xi(o,s,l,K):(s=0,$i(o,new yi([l],0),K),a=o.sel);var h=r;function f(t){if(0!=tt(h,t))if(h=t,"rectangle"==n.unit){for(var i=[],u=e.options.tabSize,c=z(Xe(o,r.line).text,r.ch,u),f=z(Xe(o,t.line).text,t.ch,u),d=Math.min(c,f),p=Math.max(c,f),g=Math.min(r.line,t.line),v=Math.min(e.lastLine(),Math.max(r.line,t.line));g<=v;g++){var m=Xe(o,g).text,y=X(m,d,u);d==p?i.push(new bi(et(g,y),et(g,y))):m.length>y&&i.push(new bi(et(g,y),et(g,X(m,p,u))))}i.length||i.push(new bi(r,r)),$i(o,wi(e,a.ranges.slice(0,s).concat(i),s),{origin:"*mouse",scroll:!1}),e.scrollIntoView(t)}else{var b,w=l,x=dl(e,t,n.unit),C=w.anchor;tt(x.anchor,C)>0?(b=x.head,C=ot(w.from(),x.anchor)):(b=x.anchor,C=it(w.to(),x.head));var S=a.ranges.slice(0);S[s]=function(e,t){var r=t.anchor,n=t.head,i=Xe(e.doc,r.line);if(0==tt(r,n)&&r.sticky==n.sticky)return t;var o=ce(i);if(!o)return t;var l=ae(o,r.ch,r.sticky),s=o[l];if(s.from!=r.ch&&s.to!=r.ch)return t;var a,u=l+(s.from==r.ch==(1!=s.level)?0:1);if(0==u||u==o.length)return t;if(n.line!=r.line)a=(n.line-r.line)*("ltr"==e.doc.direction?1:-1)>0;else{var c=ae(o,n.ch,n.sticky),h=c-l||(n.ch-r.ch)*(1==s.level?-1:1);a=c==u-1||c==u?h<0:h>0}var f=o[u+(a?-1:0)],d=a==(1==f.level),p=d?f.from:f.to,g=d?"after":"before";return r.ch==p&&r.sticky==g?t:new bi(new et(r.line,p,g),n)}(e,new bi(st(o,C),b)),$i(o,wi(e,S,s),K)}}var d=i.wrapper.getBoundingClientRect(),p=0;function g(t){e.state.selectingText=!1,p=1/0,t&&(we(t),i.input.focus()),pe(i.wrapper.ownerDocument,"mousemove",v),pe(i.wrapper.ownerDocument,"mouseup",m),o.history.lastSelOrigin=null}var v=Zn(e,function(t){0!==t.buttons&&ke(t)?function t(r){var l=++p;var s=sn(e,r,!0,"rectangle"==n.unit);if(!s)return;if(0!=tt(s,h)){e.curOp.focus=W(),f(s);var a=Tn(i,o);(s.line>=a.to||s.lined.bottom?20:0;u&&setTimeout(Zn(e,function(){p==l&&(i.scroller.scrollTop+=u,t(r))}),50)}}(t):g(t)}),m=Zn(e,g);e.state.selectingText=m,fe(i.wrapper.ownerDocument,"mousemove",v),fe(i.wrapper.ownerDocument,"mouseup",m)}(e,n,t,o)}(t,n,o,e):Le(e)==r.scroller&&we(e):2==i?(n&&Ki(t.doc,n),setTimeout(function(){return r.input.focus()},20)):3==i&&(S?t.display.input.onContextMenu(e):xn(t)))}}function dl(e,t,r){if("char"==r)return new bi(t,t);if("word"==r)return e.findWordAt(t);if("line"==r)return new bi(et(t.line,0),st(e.doc,et(t.line+1,0)));var n=r(e,t);return new bi(n.from,n.to)}function pl(e,t,r,n){var i,o;if(t.touches)i=t.touches[0].clientX,o=t.touches[0].clientY;else try{i=t.clientX,o=t.clientY}catch(t){return!1}if(i>=Math.floor(e.display.gutters.getBoundingClientRect().right))return!1;n&&we(t);var l=e.display,s=l.lineDiv.getBoundingClientRect();if(o>s.bottom||!ye(e,r))return Ce(t);o-=s.top-l.viewOffset;for(var a=0;a=i)return ge(e,r,e,Ze(e.doc,o),e.display.gutterSpecs[a].className,t),Ce(t)}}function gl(e,t){return pl(e,t,"gutterClick",!0)}function vl(e,t){xr(e.display,t)||function(e,t){if(!ye(e,"gutterContextMenu"))return!1;return pl(e,t,"gutterContextMenu",!1)}(e,t)||ve(e,t,"contextmenu")||S||e.display.input.onContextMenu(t)}function ml(e){e.display.wrapper.className=e.display.wrapper.className.replace(/\s*cm-s-\S+/g,"")+e.options.theme.replace(/(^|\s)\s*/g," cm-s-"),Rr(e)}hl.prototype.compare=function(e,t,r){return this.time+400>e&&0==tt(t,this.pos)&&r==this.button};var yl={toString:function(){return"CodeMirror.Init"}},bl={},wl={};function xl(e,t,r){if(!t!=!(r&&r!=yl)){var n=e.display.dragFunctions,i=t?fe:pe;i(e.display.scroller,"dragstart",n.start),i(e.display.scroller,"dragenter",n.enter),i(e.display.scroller,"dragover",n.over),i(e.display.scroller,"dragleave",n.leave),i(e.display.scroller,"drop",n.drop)}}function Cl(e){e.options.lineWrapping?(H(e.display.wrapper,"CodeMirror-wrap"),e.display.sizer.style.minWidth="",e.display.sizerWidth=null):(T(e.display.wrapper,"CodeMirror-wrap"),jt(e)),ln(e),un(e),Rr(e),setTimeout(function(){return Rn(e)},100)}function Sl(e,t){var n=this;if(!(this instanceof Sl))return new Sl(e,t);this.options=t=t?I(t):{},I(bl,t,!1);var i=t.value;"string"==typeof i?i=new Mo(i,t.mode,null,t.lineSeparator,t.direction):t.mode&&(i.modeOption=t.mode),this.doc=i;var o=new Sl.inputStyles[t.inputStyle](this),u=this.display=new function(e,t,n,i){var o=this;this.input=n,o.scrollbarFiller=O("div",null,"CodeMirror-scrollbar-filler"),o.scrollbarFiller.setAttribute("cm-not-content","true"),o.gutterFiller=O("div",null,"CodeMirror-gutter-filler"),o.gutterFiller.setAttribute("cm-not-content","true"),o.lineDiv=A("div",null,"CodeMirror-code"),o.selectionDiv=O("div",null,null,"position: relative; z-index: 1"),o.cursorDiv=O("div",null,"CodeMirror-cursors"),o.measure=O("div",null,"CodeMirror-measure"),o.lineMeasure=O("div",null,"CodeMirror-measure"),o.lineSpace=A("div",[o.measure,o.lineMeasure,o.selectionDiv,o.cursorDiv,o.lineDiv],null,"position: relative; outline: none");var u=A("div",[o.lineSpace],"CodeMirror-lines");o.mover=O("div",[u],null,"position: relative"),o.sizer=O("div",[o.mover],"CodeMirror-sizer"),o.sizerWidth=null,o.heightForcer=O("div",null,null,"position: absolute; height: "+G+"px; width: 1px;"),o.gutters=O("div",null,"CodeMirror-gutters"),o.lineGutter=null,o.scroller=O("div",[o.sizer,o.heightForcer,o.gutters],"CodeMirror-scroll"),o.scroller.setAttribute("tabIndex","-1"),o.wrapper=O("div",[o.scrollbarFiller,o.gutterFiller,o.scroller],"CodeMirror"),l&&s<8&&(o.gutters.style.zIndex=-1,o.scroller.style.paddingRight=0),a||r&&m||(o.scroller.draggable=!0),e&&(e.appendChild?e.appendChild(o.wrapper):e(o.wrapper)),o.viewFrom=o.viewTo=t.first,o.reportedViewFrom=o.reportedViewTo=t.first,o.view=[],o.renderedView=null,o.externalMeasured=null,o.viewOffset=0,o.lastWrapHeight=o.lastWrapWidth=0,o.updateLineNumbers=null,o.nativeBarWidth=o.barHeight=o.barWidth=0,o.scrollbarsClipped=!1,o.lineNumWidth=o.lineNumInnerWidth=o.lineNumChars=null,o.alignWidgets=!1,o.cachedCharWidth=o.cachedTextHeight=o.cachedPaddingH=null,o.maxLine=null,o.maxLineLength=0,o.maxLineChanged=!1,o.wheelDX=o.wheelDY=o.wheelStartX=o.wheelStartY=null,o.shift=!1,o.selForContextMenu=null,o.activeTouch=null,o.gutterSpecs=ci(i.gutters,i.lineNumbers),hi(o),n.init(o)}(e,i,o,t);for(var c in u.wrapper.CodeMirror=this,ml(this),t.lineWrapping&&(this.display.wrapper.className+=" CodeMirror-wrap"),Un(this),this.state={keyMaps:[],overlays:[],modeGen:0,overwrite:!1,delayingBlurEvent:!1,focused:!1,suppressEdits:!1,pasteIncoming:-1,cutIncoming:-1,selectingText:!1,draggingText:!1,highlight:new R,keySeq:null,specialChars:null},t.autofocus&&!m&&u.input.focus(),l&&s<11&&setTimeout(function(){return n.display.input.reset(!0)},20),function(e){var t=e.display;fe(t.scroller,"mousedown",Zn(e,fl)),fe(t.scroller,"dblclick",l&&s<11?Zn(e,function(t){if(!ve(e,t)){var r=sn(e,t);if(r&&!gl(e,t)&&!xr(e.display,t)){we(t);var n=e.findWordAt(r);Ki(e.doc,n.anchor,n.head)}}}):function(t){return ve(e,t)||we(t)});fe(t.scroller,"contextmenu",function(t){return vl(e,t)});var r,n={end:0};function i(){t.activeTouch&&(r=setTimeout(function(){return t.activeTouch=null},1e3),(n=t.activeTouch).end=+new Date)}function o(e,t){if(null==t.left)return!0;var r=t.left-e.left,n=t.top-e.top;return r*r+n*n>400}fe(t.scroller,"touchstart",function(i){if(!ve(e,i)&&!function(e){if(1!=e.touches.length)return!1;var t=e.touches[0];return t.radiusX<=1&&t.radiusY<=1}(i)&&!gl(e,i)){t.input.ensurePolled(),clearTimeout(r);var o=+new Date;t.activeTouch={start:o,moved:!1,prev:o-n.end<=300?n:null},1==i.touches.length&&(t.activeTouch.left=i.touches[0].pageX,t.activeTouch.top=i.touches[0].pageY)}}),fe(t.scroller,"touchmove",function(){t.activeTouch&&(t.activeTouch.moved=!0)}),fe(t.scroller,"touchend",function(r){var n=t.activeTouch;if(n&&!xr(t,r)&&null!=n.left&&!n.moved&&new Date-n.start<300){var l,s=e.coordsChar(t.activeTouch,"page");l=!n.prev||o(n,n.prev)?new bi(s,s):!n.prev.prev||o(n,n.prev.prev)?e.findWordAt(s):new bi(et(s.line,0),st(e.doc,et(s.line+1,0))),e.setSelection(l.anchor,l.head),e.focus(),we(r)}i()}),fe(t.scroller,"touchcancel",i),fe(t.scroller,"scroll",function(){t.scroller.clientHeight&&(Hn(e,t.scroller.scrollTop),Pn(e,t.scroller.scrollLeft,!0),ge(e,"scroll",e))}),fe(t.scroller,"mousewheel",function(t){return mi(e,t)}),fe(t.scroller,"DOMMouseScroll",function(t){return mi(e,t)}),fe(t.wrapper,"scroll",function(){return t.wrapper.scrollTop=t.wrapper.scrollLeft=0}),t.dragFunctions={enter:function(t){ve(e,t)||Se(t)},over:function(t){ve(e,t)||(!function(e,t){var r=sn(e,t);if(r){var n=document.createDocumentFragment();vn(e,r,n),e.display.dragCursor||(e.display.dragCursor=O("div",null,"CodeMirror-cursors CodeMirror-dragcursors"),e.display.lineSpace.insertBefore(e.display.dragCursor,e.display.cursorDiv)),N(e.display.dragCursor,n)}}(e,t),Se(t))},start:function(t){return function(e,t){if(l&&(!e.state.draggingText||+new Date-No<100))Se(t);else if(!ve(e,t)&&!xr(e.display,t)&&(t.dataTransfer.setData("Text",e.getSelection()),t.dataTransfer.effectAllowed="copyMove",t.dataTransfer.setDragImage&&!f)){var r=O("img",null,null,"position: fixed; left: 0; top: 0;");r.src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==",h&&(r.width=r.height=1,e.display.wrapper.appendChild(r),r._top=r.offsetTop),t.dataTransfer.setDragImage(r,0,0),h&&r.parentNode.removeChild(r)}}(e,t)},drop:Zn(e,Oo),leave:function(t){ve(e,t)||Ao(e)}};var a=t.input.getField();fe(a,"keyup",function(t){return sl.call(e,t)}),fe(a,"keydown",Zn(e,ll)),fe(a,"keypress",Zn(e,al)),fe(a,"focus",function(t){return Cn(e,t)}),fe(a,"blur",function(t){return Sn(e,t)})}(this),Ho(),Kn(this),this.curOp.forceUpdate=!0,Di(this,i),t.autofocus&&!m||this.hasFocus()?setTimeout(E(Cn,this),20):Sn(this),wl)wl.hasOwnProperty(c)&&wl[c](n,t[c],yl);ui(this),t.finishInit&&t.finishInit(this);for(var d=0;d150)){if(!n)return;r="prev"}}else u=0,r="not";"prev"==r?u=t>o.first?z(Xe(o,t-1).text,null,l):0:"add"==r?u=a+e.options.indentUnit:"subtract"==r?u=a-e.options.indentUnit:"number"==typeof r&&(u=a+r),u=Math.max(0,u);var h="",f=0;if(e.options.indentWithTabs)for(var d=Math.floor(u/l);d;--d)f+=l,h+="\t";if(fl,a=We(t),u=null;if(s&&n.ranges.length>1)if(Tl&&Tl.text.join("\n")==t){if(n.ranges.length%Tl.text.length==0){u=[];for(var c=0;c=0;f--){var d=n.ranges[f],p=d.from(),g=d.to();d.empty()&&(r&&r>0?p=et(p.line,p.ch-r):e.state.overwrite&&!s?g=et(g.line,Math.min(Xe(o,g.line).text.length,g.ch+$(a).length)):s&&Tl&&Tl.lineWise&&Tl.text.join("\n")==t&&(p=g=et(p.line,0)));var v={from:p,to:g,text:u?u[f%u.length]:a,origin:i||(s?"paste":e.state.cutIncoming>l?"cut":"+input")};oo(e.doc,v),sr(e,"inputRead",e,v)}t&&!s&&Al(e,t),On(e),e.curOp.updateInput<2&&(e.curOp.updateInput=h),e.curOp.typing=!0,e.state.pasteIncoming=e.state.cutIncoming=-1}function Ol(e,t){var r=e.clipboardData&&e.clipboardData.getData("Text");if(r)return e.preventDefault(),t.isReadOnly()||t.options.disableInput||qn(t,function(){return Nl(t,r,0,null,"paste")}),!0}function Al(e,t){if(e.options.electricChars&&e.options.smartIndent)for(var r=e.doc.sel,n=r.ranges.length-1;n>=0;n--){var i=r.ranges[n];if(!(i.head.ch>100||n&&r.ranges[n-1].head.line==i.head.line)){var o=e.getModeAt(i.head),l=!1;if(o.electricChars){for(var s=0;s-1){l=kl(e,i.head.line,"smart");break}}else o.electricInput&&o.electricInput.test(Xe(e.doc,i.head.line).text.slice(0,i.head.ch))&&(l=kl(e,i.head.line,"smart"));l&&sr(e,"electricInput",e,i.head.line)}}}function Dl(e){for(var t=[],r=[],n=0;n=t.text.length?(r.ch=t.text.length,r.sticky="before"):r.ch<=0&&(r.ch=0,r.sticky="after");var o=ae(i,r.ch,r.sticky),l=i[o];if("ltr"==e.doc.direction&&l.level%2==0&&(n>0?l.to>r.ch:l.from=l.from&&f>=c.begin)){var d=h?"before":"after";return new et(r.line,f,d)}}var p=function(e,t,n){for(var o=function(e,t){return t?new et(r.line,a(e,1),"before"):new et(r.line,e,"after")};e>=0&&e0==(1!=l.level),u=s?n.begin:a(n.end,-1);if(l.from<=u&&u0?c.end:a(c.begin,-1);return null==v||n>0&&v==t.text.length||!(g=p(n>0?0:i.length-1,n,u(v)))?null:g}(e.cm,s,t,r):$o(s,t,r))){if(n||(l=t.line+r)=e.first+e.size||(t=new et(l,t.ch,t.sticky),!(s=Xe(e,l))))return!1;t=qo(i,e.cm,s,t.line,r)}else t=o;return!0}if("char"==n)a();else if("column"==n)a(!0);else if("word"==n||"group"==n)for(var u=null,c="group"==n,h=e.cm&&e.cm.getHelper(t,"wordChars"),f=!0;!(r<0)||a(!f);f=!1){var d=s.text.charAt(t.ch)||"\n",p=te(d,h)?"w":c&&"\n"==d?"n":!c||/\s/.test(d)?null:"p";if(!c||f||p||(p="s"),u&&u!=p){r<0&&(r=1,a(),t.sticky="after");break}if(p&&(u=p),r>0&&!a(!f))break}var g=to(e,t,o,l,!0);return rt(o,g)&&(g.hitSide=!0),g}function Pl(e,t,r,n){var i,o,l=e.doc,s=t.left;if("page"==n){var a=Math.min(e.display.wrapper.clientHeight,window.innerHeight||document.documentElement.clientHeight),u=Math.max(a-.5*en(e.display),3);i=(r>0?t.bottom:t.top)+r*u}else"line"==n&&(i=r>0?t.bottom+3:t.top-3);for(;(o=$r(e,s,i)).outside;){if(r<0?i<=0:i>=l.height){o.hitSide=!0;break}i+=5*r}return o}var El=function(e){this.cm=e,this.lastAnchorNode=this.lastAnchorOffset=this.lastFocusNode=this.lastFocusOffset=null,this.polling=new R,this.composing=null,this.gracePeriod=!1,this.readDOMTimeout=null};function Il(e,t){var r=Ar(e,t.line);if(!r||r.hidden)return null;var n=Xe(e.doc,t.line),i=Nr(r,n,t.line),o=ce(n,e.doc.direction),l="left";o&&(l=ae(o,t.ch)%2?"right":"left");var s=Pr(i.map,t.ch,l);return s.offset="right"==s.collapse?s.end:s.start,s}function zl(e,t){return t&&(e.bad=!0),e}function Rl(e,t,r){var n;if(t==e.display.lineDiv){if(!(n=e.display.lineDiv.childNodes[r]))return zl(e.clipPos(et(e.display.viewTo-1)),!0);t=null,r=0}else for(n=t;;n=n.parentNode){if(!n||n==e.display.lineDiv)return null;if(n.parentNode&&n.parentNode==e.display.lineDiv)break}for(var i=0;i=t.display.viewTo||o.line=t.display.viewFrom&&Il(t,i)||{node:a[0].measure.map[2],offset:0},c=o.linen.firstLine()&&(l=et(l.line-1,Xe(n.doc,l.line-1).length)),s.ch==Xe(n.doc,s.line).text.length&&s.linei.viewTo-1)return!1;l.line==i.viewFrom||0==(e=an(n,l.line))?(t=qe(i.view[0].line),r=i.view[0].node):(t=qe(i.view[e].line),r=i.view[e-1].node.nextSibling);var a,u,c=an(n,s.line);if(c==i.view.length-1?(a=i.viewTo-1,u=i.lineDiv.lastChild):(a=qe(i.view[c+1].line)-1,u=i.view[c+1].node.previousSibling),!r)return!1;for(var h=n.doc.splitLines(function(e,t,r,n,i){var o="",l=!1,s=e.doc.lineSeparator(),a=!1;function u(){l&&(o+=s,a&&(o+=s),l=a=!1)}function c(e){e&&(u(),o+=e)}function h(t){if(1==t.nodeType){var r=t.getAttribute("cm-text");if(r)return void c(r);var o,f=t.getAttribute("cm-marker");if(f){var d=e.findMarks(et(n,0),et(i+1,0),(v=+f,function(e){return e.id==v}));return void(d.length&&(o=d[0].find(0))&&c(Ye(e.doc,o.from,o.to).join(s)))}if("false"==t.getAttribute("contenteditable"))return;var p=/^(pre|div|p|li|table|br)$/i.test(t.nodeName);if(!/^br$/i.test(t.nodeName)&&0==t.textContent.length)return;p&&u();for(var g=0;g1&&f.length>1;)if($(h)==$(f))h.pop(),f.pop(),a--;else{if(h[0]!=f[0])break;h.shift(),f.shift(),t++}for(var d=0,p=0,g=h[0],v=f[0],m=Math.min(g.length,v.length);dl.ch&&y.charCodeAt(y.length-p-1)==b.charCodeAt(b.length-p-1);)d--,p++;h[h.length-1]=y.slice(0,y.length-p).replace(/^\u200b+/,""),h[0]=h[0].slice(d).replace(/\u200b+$/,"");var x=et(t,d),C=et(a,f.length?$(f).length-p:0);return h.length>1||h[0]||tt(x,C)?(co(n.doc,h,x,C,"+input"),!0):void 0},El.prototype.ensurePolled=function(){this.forceCompositionEnd()},El.prototype.reset=function(){this.forceCompositionEnd()},El.prototype.forceCompositionEnd=function(){this.composing&&(clearTimeout(this.readDOMTimeout),this.composing=null,this.updateFromDOM(),this.div.blur(),this.div.focus())},El.prototype.readFromDOMSoon=function(){var e=this;null==this.readDOMTimeout&&(this.readDOMTimeout=setTimeout(function(){if(e.readDOMTimeout=null,e.composing){if(!e.composing.done)return;e.composing=null}e.updateFromDOM()},80))},El.prototype.updateFromDOM=function(){var e=this;!this.cm.isReadOnly()&&this.pollContent()||qn(this.cm,function(){return un(e.cm)})},El.prototype.setUneditable=function(e){e.contentEditable="false"},El.prototype.onKeyPress=function(e){0==e.charCode||this.composing||(e.preventDefault(),this.cm.isReadOnly()||Zn(this.cm,Nl)(this.cm,String.fromCharCode(null==e.charCode?e.keyCode:e.charCode),0))},El.prototype.readOnlyChanged=function(e){this.div.contentEditable=String("nocursor"!=e)},El.prototype.onContextMenu=function(){},El.prototype.resetPosition=function(){},El.prototype.needsContentAttribute=!0;var Gl=function(e){this.cm=e,this.prevInput="",this.pollingFast=!1,this.polling=new R,this.hasSelection=!1,this.composing=null};Gl.prototype.init=function(e){var t=this,r=this,n=this.cm;this.createField(e);var i=this.textarea;function o(e){if(!ve(n,e)){if(n.somethingSelected())Ml({lineWise:!1,text:n.getSelections()});else{if(!n.options.lineWiseCopyCut)return;var t=Dl(n);Ml({lineWise:!0,text:t.text}),"cut"==e.type?n.setSelections(t.ranges,null,V):(r.prevInput="",i.value=t.text.join("\n"),P(i))}"cut"==e.type&&(n.state.cutIncoming=+new Date)}}e.wrapper.insertBefore(this.wrapper,e.wrapper.firstChild),g&&(i.style.width="0px"),fe(i,"input",function(){l&&s>=9&&t.hasSelection&&(t.hasSelection=null),r.poll()}),fe(i,"paste",function(e){ve(n,e)||Ol(e,n)||(n.state.pasteIncoming=+new Date,r.fastPoll())}),fe(i,"cut",o),fe(i,"copy",o),fe(e.scroller,"paste",function(t){if(!xr(e,t)&&!ve(n,t)){if(!i.dispatchEvent)return n.state.pasteIncoming=+new Date,void r.focus();var o=new Event("paste");o.clipboardData=t.clipboardData,i.dispatchEvent(o)}}),fe(e.lineSpace,"selectstart",function(t){xr(e,t)||we(t)}),fe(i,"compositionstart",function(){var e=n.getCursor("from");r.composing&&r.composing.range.clear(),r.composing={start:e,range:n.markText(e,n.getCursor("to"),{className:"CodeMirror-composing"})}}),fe(i,"compositionend",function(){r.composing&&(r.poll(),r.composing.range.clear(),r.composing=null)})},Gl.prototype.createField=function(e){this.wrapper=Hl(),this.textarea=this.wrapper.firstChild},Gl.prototype.prepareSelection=function(){var e=this.cm,t=e.display,r=e.doc,n=gn(e);if(e.options.moveInputWithCursor){var i=Xr(e,r.sel.primary().head,"div"),o=t.wrapper.getBoundingClientRect(),l=t.lineDiv.getBoundingClientRect();n.teTop=Math.max(0,Math.min(t.wrapper.clientHeight-10,i.top+l.top-o.top)),n.teLeft=Math.max(0,Math.min(t.wrapper.clientWidth-10,i.left+l.left-o.left))}return n},Gl.prototype.showSelection=function(e){var t=this.cm.display;N(t.cursorDiv,e.cursors),N(t.selectionDiv,e.selection),null!=e.teTop&&(this.wrapper.style.top=e.teTop+"px",this.wrapper.style.left=e.teLeft+"px")},Gl.prototype.reset=function(e){if(!this.contextMenuPending&&!this.composing){var t=this.cm;if(t.somethingSelected()){this.prevInput="";var r=t.getSelection();this.textarea.value=r,t.state.focused&&P(this.textarea),l&&s>=9&&(this.hasSelection=r)}else e||(this.prevInput=this.textarea.value="",l&&s>=9&&(this.hasSelection=null))}},Gl.prototype.getField=function(){return this.textarea},Gl.prototype.supportsTouch=function(){return!1},Gl.prototype.focus=function(){if("nocursor"!=this.cm.options.readOnly&&(!m||W()!=this.textarea))try{this.textarea.focus()}catch(e){}},Gl.prototype.blur=function(){this.textarea.blur()},Gl.prototype.resetPosition=function(){this.wrapper.style.top=this.wrapper.style.left=0},Gl.prototype.receivedFocus=function(){this.slowPoll()},Gl.prototype.slowPoll=function(){var e=this;this.pollingFast||this.polling.set(this.cm.options.pollInterval,function(){e.poll(),e.cm.state.focused&&e.slowPoll()})},Gl.prototype.fastPoll=function(){var e=!1,t=this;t.pollingFast=!0,t.polling.set(20,function r(){t.poll()||e?(t.pollingFast=!1,t.slowPoll()):(e=!0,t.polling.set(60,r))})},Gl.prototype.poll=function(){var e=this,t=this.cm,r=this.textarea,n=this.prevInput;if(this.contextMenuPending||!t.state.focused||He(r)&&!n&&!this.composing||t.isReadOnly()||t.options.disableInput||t.state.keySeq)return!1;var i=r.value;if(i==n&&!t.somethingSelected())return!1;if(l&&s>=9&&this.hasSelection===i||y&&/[\uf700-\uf7ff]/.test(i))return t.display.input.reset(),!1;if(t.doc.sel==t.display.selForContextMenu){var o=i.charCodeAt(0);if(8203!=o||n||(n="​"),8666==o)return this.reset(),this.cm.execCommand("undo")}for(var a=0,u=Math.min(n.length,i.length);a1e3||i.indexOf("\n")>-1?r.value=e.prevInput="":e.prevInput=i,e.composing&&(e.composing.range.clear(),e.composing.range=t.markText(e.composing.start,t.getCursor("to"),{className:"CodeMirror-composing"}))}),!0},Gl.prototype.ensurePolled=function(){this.pollingFast&&this.poll()&&(this.pollingFast=!1)},Gl.prototype.onKeyPress=function(){l&&s>=9&&(this.hasSelection=null),this.fastPoll()},Gl.prototype.onContextMenu=function(e){var t=this,r=t.cm,n=r.display,i=t.textarea;t.contextMenuPending&&t.contextMenuPending();var o=sn(r,e),u=n.scroller.scrollTop;if(o&&!h){r.options.resetSelectionOnContextMenu&&-1==r.doc.sel.contains(o)&&Zn(r,$i)(r.doc,xi(o),V);var c,f=i.style.cssText,d=t.wrapper.style.cssText,p=t.wrapper.offsetParent.getBoundingClientRect();if(t.wrapper.style.cssText="position: static",i.style.cssText="position: absolute; width: 30px; height: 30px;\n top: "+(e.clientY-p.top-5)+"px; left: "+(e.clientX-p.left-5)+"px;\n z-index: 1000; background: "+(l?"rgba(255, 255, 255, .05)":"transparent")+";\n outline: none; border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);",a&&(c=window.scrollY),n.input.focus(),a&&window.scrollTo(null,c),n.input.reset(),r.somethingSelected()||(i.value=t.prevInput=" "),t.contextMenuPending=m,n.selForContextMenu=r.doc.sel,clearTimeout(n.detectingSelectAll),l&&s>=9&&v(),S){Se(e);var g=function(){pe(window,"mouseup",g),setTimeout(m,20)};fe(window,"mouseup",g)}else setTimeout(m,50)}function v(){if(null!=i.selectionStart){var e=r.somethingSelected(),o="​"+(e?i.value:"");i.value="⇚",i.value=o,t.prevInput=e?"":"​",i.selectionStart=1,i.selectionEnd=o.length,n.selForContextMenu=r.doc.sel}}function m(){if(t.contextMenuPending==m&&(t.contextMenuPending=!1,t.wrapper.style.cssText=d,i.style.cssText=f,l&&s<9&&n.scrollbars.setScrollTop(n.scroller.scrollTop=u),null!=i.selectionStart)){(!l||l&&s<9)&&v();var e=0,o=function(){n.selForContextMenu==r.doc.sel&&0==i.selectionStart&&i.selectionEnd>0&&"​"==t.prevInput?Zn(r,no)(r):e++<10?n.detectingSelectAll=setTimeout(o,500):(n.selForContextMenu=null,n.input.reset())};n.detectingSelectAll=setTimeout(o,200)}}},Gl.prototype.readOnlyChanged=function(e){e||this.reset(),this.textarea.disabled="nocursor"==e},Gl.prototype.setUneditable=function(){},Gl.prototype.needsContentAttribute=!1,function(e){var t=e.optionHandlers;function r(r,n,i,o){e.defaults[r]=n,i&&(t[r]=o?function(e,t,r){r!=yl&&i(e,t,r)}:i)}e.defineOption=r,e.Init=yl,r("value","",function(e,t){return e.setValue(t)},!0),r("mode",null,function(e,t){e.doc.modeOption=t,Ti(e)},!0),r("indentUnit",2,Ti,!0),r("indentWithTabs",!1),r("smartIndent",!0),r("tabSize",4,function(e){Mi(e),Rr(e),un(e)},!0),r("lineSeparator",null,function(e,t){if(e.doc.lineSep=t,t){var r=[],n=e.doc.first;e.doc.iter(function(e){for(var i=0;;){var o=e.text.indexOf(t,i);if(-1==o)break;i=o+t.length,r.push(et(n,o))}n++});for(var i=r.length-1;i>=0;i--)co(e.doc,t,r[i],et(r[i].line,r[i].ch+t.length))}}),r("specialChars",/[\u0000-\u001f\u007f-\u009f\u00ad\u061c\u200b-\u200f\u2028\u2029\ufeff\ufff9-\ufffc]/g,function(e,t,r){e.state.specialChars=new RegExp(t.source+(t.test("\t")?"":"|\t"),"g"),r!=yl&&e.refresh()}),r("specialCharPlaceholder",Qt,function(e){return e.refresh()},!0),r("electricChars",!0),r("inputStyle",m?"contenteditable":"textarea",function(){throw new Error("inputStyle can not (yet) be changed in a running editor")},!0),r("spellcheck",!1,function(e,t){return e.getInputField().spellcheck=t},!0),r("autocorrect",!1,function(e,t){return e.getInputField().autocorrect=t},!0),r("autocapitalize",!1,function(e,t){return e.getInputField().autocapitalize=t},!0),r("rtlMoveVisually",!w),r("wholeLineUpdateBefore",!0),r("theme","default",function(e){ml(e),fi(e)},!0),r("keyMap","default",function(e,t,r){var n=Xo(t),i=r!=yl&&Xo(r);i&&i.detach&&i.detach(e,n),n.attach&&n.attach(e,i||null)}),r("extraKeys",null),r("configureMouse",null),r("lineWrapping",!1,Cl,!0),r("gutters",[],function(e,t){e.display.gutterSpecs=ci(t,e.options.lineNumbers),fi(e)},!0),r("fixedGutter",!0,function(e,t){e.display.gutters.style.left=t?nn(e.display)+"px":"0",e.refresh()},!0),r("coverGutterNextToScrollbar",!1,function(e){return Rn(e)},!0),r("scrollbarStyle","native",function(e){Un(e),Rn(e),e.display.scrollbars.setScrollTop(e.doc.scrollTop),e.display.scrollbars.setScrollLeft(e.doc.scrollLeft)},!0),r("lineNumbers",!1,function(e,t){e.display.gutterSpecs=ci(e.options.gutters,t),fi(e)},!0),r("firstLineNumber",1,fi,!0),r("lineNumberFormatter",function(e){return e},fi,!0),r("showCursorWhenSelecting",!1,pn,!0),r("resetSelectionOnContextMenu",!0),r("lineWiseCopyCut",!0),r("pasteLinesPerSelection",!0),r("selectionsMayTouch",!1),r("readOnly",!1,function(e,t){"nocursor"==t&&(Sn(e),e.display.input.blur()),e.display.input.readOnlyChanged(t)}),r("disableInput",!1,function(e,t){t||e.display.input.reset()},!0),r("dragDrop",!0,xl),r("allowDropFileTypes",null),r("cursorBlinkRate",530),r("cursorScrollMargin",0),r("cursorHeight",1,pn,!0),r("singleCursorHeightPerLine",!0,pn,!0),r("workTime",100),r("workDelay",100),r("flattenSpans",!0,Mi,!0),r("addModeClass",!1,Mi,!0),r("pollInterval",100),r("undoDepth",200,function(e,t){return e.doc.history.undoDepth=t}),r("historyEventDelay",1250),r("viewportMargin",10,function(e){return e.refresh()},!0),r("maxHighlightLength",1e4,Mi,!0),r("moveInputWithCursor",!0,function(e,t){t||e.display.input.resetPosition()}),r("tabindex",null,function(e,t){return e.display.input.getField().tabIndex=t||""}),r("autofocus",null),r("direction","ltr",function(e,t){return e.doc.setDirection(t)},!0),r("phrases",null)}(Sl),function(e){var t=e.optionHandlers,r=e.helpers={};e.prototype={constructor:e,focus:function(){window.focus(),this.display.input.focus()},setOption:function(e,r){var n=this.options,i=n[e];n[e]==r&&"mode"!=e||(n[e]=r,t.hasOwnProperty(e)&&Zn(this,t[e])(this,r,i),ge(this,"optionChange",this,e))},getOption:function(e){return this.options[e]},getDoc:function(){return this.doc},addKeyMap:function(e,t){this.state.keyMaps[t?"push":"unshift"](Xo(e))},removeKeyMap:function(e){for(var t=this.state.keyMaps,r=0;rr&&(kl(this,i.head.line,e,!0),r=i.head.line,n==this.doc.sel.primIndex&&On(this));else{var o=i.from(),l=i.to(),s=Math.max(r,o.line);r=Math.min(this.lastLine(),l.line-(l.ch?0:1))+1;for(var a=s;a0&&Xi(this.doc,n,new bi(o,u[n].to()),V)}}}),getTokenAt:function(e,t){return yt(this,e,t)},getLineTokens:function(e,t){return yt(this,et(e),t,!0)},getTokenTypeAt:function(e){e=st(this.doc,e);var t,r=ft(this,Xe(this.doc,e.line)),n=0,i=(r.length-1)/2,o=e.ch;if(0==o)t=r[2];else for(;;){var l=n+i>>1;if((l?r[2*l-1]:0)>=o)i=l;else{if(!(r[2*l+1]o&&(e=o,i=!0),n=Xe(this.doc,e)}else n=e;return Vr(this,n,{top:0,left:0},t||"page",r||i).top+(i?this.doc.height-Vt(n):0)},defaultTextHeight:function(){return en(this.display)},defaultCharWidth:function(){return tn(this.display)},getViewport:function(){return{from:this.display.viewFrom,to:this.display.viewTo}},addWidget:function(e,t,r,n,i){var o,l,s,a=this.display,u=(e=Xr(this,st(this.doc,e))).bottom,c=e.left;if(t.style.position="absolute",t.setAttribute("cm-ignore-events","true"),this.display.input.setUneditable(t),a.sizer.appendChild(t),"over"==n)u=e.top;else if("above"==n||"near"==n){var h=Math.max(a.wrapper.clientHeight,this.doc.height),f=Math.max(a.sizer.clientWidth,a.lineSpace.clientWidth);("above"==n||e.bottom+t.offsetHeight>h)&&e.top>t.offsetHeight?u=e.top-t.offsetHeight:e.bottom+t.offsetHeight<=h&&(u=e.bottom),c+t.offsetWidth>f&&(c=f-t.offsetWidth)}t.style.top=u+"px",t.style.left=t.style.right="","right"==i?(c=a.sizer.clientWidth-t.offsetWidth,t.style.right="0px"):("left"==i?c=0:"middle"==i&&(c=(a.sizer.clientWidth-t.offsetWidth)/2),t.style.left=c+"px"),r&&(o=this,l={left:c,top:u,right:c+t.offsetWidth,bottom:u+t.offsetHeight},null!=(s=Mn(o,l)).scrollTop&&Hn(o,s.scrollTop),null!=s.scrollLeft&&Pn(o,s.scrollLeft))},triggerOnKeyDown:Qn(ll),triggerOnKeyPress:Qn(al),triggerOnKeyUp:sl,triggerOnMouseDown:Qn(fl),execCommand:function(e){if(Zo.hasOwnProperty(e))return Zo[e].call(null,this)},triggerElectric:Qn(function(e){Al(this,e)}),findPosH:function(e,t,r,n){var i=1;t<0&&(i=-1,t=-t);for(var o=st(this.doc,e),l=0;l0&&l(t.charAt(r-1));)--r;for(;n.5)&&ln(this),ge(this,"refresh",this)}),swapDoc:Qn(function(e){var t=this.doc;return t.cm=null,this.state.selectingText&&this.state.selectingText(),Di(this,e),Rr(this),this.display.input.reset(),An(this,e.scrollLeft,e.scrollTop),this.curOp.forceScroll=!0,sr(this,"swapDoc",this,t),t}),phrase:function(e){var t=this.options.phrases;return t&&Object.prototype.hasOwnProperty.call(t,e)?t[e]:e},getInputField:function(){return this.display.input.getField()},getWrapperElement:function(){return this.display.wrapper},getScrollerElement:function(){return this.display.scroller},getGutterElement:function(){return this.display.gutters}},be(e),e.registerHelper=function(t,n,i){r.hasOwnProperty(t)||(r[t]=e[t]={_global:[]}),r[t][n]=i},e.registerGlobalHelper=function(t,n,i,o){e.registerHelper(t,n,o),r[t]._global.push({pred:i,val:o})}}(Sl);var Ul="iter insert remove copy getEditor constructor".split(" ");for(var Vl in Mo.prototype)Mo.prototype.hasOwnProperty(Vl)&&B(Ul,Vl)<0&&(Sl.prototype[Vl]=function(e){return function(){return e.apply(this.doc,arguments)}}(Mo.prototype[Vl]));return be(Mo),Sl.inputStyles={textarea:Gl,contenteditable:El},Sl.defineMode=function(e){Sl.defaults.mode||"null"==e||(Sl.defaults.mode=e),function(e,t){arguments.length>2&&(t.dependencies=Array.prototype.slice.call(arguments,2)),Ee[e]=t}.apply(this,arguments)},Sl.defineMIME=function(e,t){Ie[e]=t},Sl.defineMode("null",function(){return{token:function(e){return e.skipToEnd()}}}),Sl.defineMIME("text/plain","null"),Sl.defineExtension=function(e,t){Sl.prototype[e]=t},Sl.defineDocExtension=function(e,t){Mo.prototype[e]=t},Sl.fromTextArea=function(e,t){if((t=t?I(t):{}).value=e.value,!t.tabindex&&e.tabIndex&&(t.tabindex=e.tabIndex),!t.placeholder&&e.placeholder&&(t.placeholder=e.placeholder),null==t.autofocus){var r=W();t.autofocus=r==e||null!=e.getAttribute("autofocus")&&r==document.body}function n(){e.value=s.getValue()}var i;if(e.form&&(fe(e.form,"submit",n),!t.leaveSubmitMethodAlone)){var o=e.form;i=o.submit;try{var l=o.submit=function(){n(),o.submit=i,o.submit(),o.submit=l}}catch(e){}}t.finishInit=function(r){r.save=n,r.getTextArea=function(){return e},r.toTextArea=function(){r.toTextArea=isNaN,n(),e.parentNode.removeChild(r.getWrapperElement()),e.style.display="",e.form&&(pe(e.form,"submit",n),t.leaveSubmitMethodAlone||"function"!=typeof e.form.submit||(e.form.submit=i))}},e.style.display="none";var s=Sl(function(t){return e.parentNode.insertBefore(t,e.nextSibling)},t);return s},function(e){e.off=pe,e.on=fe,e.wheelEventPixels=vi,e.Doc=Mo,e.splitLines=We,e.countColumn=z,e.findColumn=X,e.isWordChar=ee,e.Pass=U,e.signal=ge,e.Line=Xt,e.changeEnd=Ci,e.scrollbarModel=Gn,e.Pos=et,e.cmpPos=tt,e.modes=Ee,e.mimeModes=Ie,e.resolveMode=ze,e.getMode=Re,e.modeExtensions=Be,e.extendMode=Ge,e.copyState=Ue,e.startState=Ke,e.innerMode=Ve,e.commands=Zo,e.keyMap=Ro,e.keyName=jo,e.isModifierKey=Vo,e.lookupKey=Uo,e.normalizeKeyMap=Go,e.StringStream=je,e.SharedTextMarker=So,e.TextMarker=xo,e.LineWidget=yo,e.e_preventDefault=we,e.e_stopPropagation=xe,e.e_stop=Se,e.addClass=H,e.contains=D,e.rmClass=T,e.keyNames=Po}(Sl),Sl.version="5.65.17",Sl}); \ No newline at end of file diff --git a/luci-app-mosdns/htdocs/luci-static/resources/codemirror5/libs/js-yaml.min.js b/luci-app-mosdns/htdocs/luci-static/resources/codemirror5/libs/js-yaml.min.js new file mode 100644 index 000000000..bdd8eef54 --- /dev/null +++ b/luci-app-mosdns/htdocs/luci-static/resources/codemirror5/libs/js-yaml.min.js @@ -0,0 +1,2 @@ +/*! js-yaml 4.1.0 https://github.com/nodeca/js-yaml @license MIT */ +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).jsyaml={})}(this,(function(e){"use strict";function t(e){return null==e}var n={isNothing:t,isObject:function(e){return"object"==typeof e&&null!==e},toArray:function(e){return Array.isArray(e)?e:t(e)?[]:[e]},repeat:function(e,t){var n,i="";for(n=0;nl&&(t=i-l+(o=" ... ").length),n-i>l&&(n=i+l-(a=" ...").length),{str:o+e.slice(t,n).replace(/\t/g,"→")+a,pos:i-t+o.length}}function l(e,t){return n.repeat(" ",t-e.length)+e}var c=function(e,t){if(t=Object.create(t||null),!e.buffer)return null;t.maxLength||(t.maxLength=79),"number"!=typeof t.indent&&(t.indent=1),"number"!=typeof t.linesBefore&&(t.linesBefore=3),"number"!=typeof t.linesAfter&&(t.linesAfter=2);for(var i,r=/\r?\n|\r|\0/g,o=[0],c=[],s=-1;i=r.exec(e.buffer);)c.push(i.index),o.push(i.index+i[0].length),e.position<=i.index&&s<0&&(s=o.length-2);s<0&&(s=o.length-1);var u,p,f="",d=Math.min(e.line+t.linesAfter,c.length).toString().length,h=t.maxLength-(t.indent+d+3);for(u=1;u<=t.linesBefore&&!(s-u<0);u++)p=a(e.buffer,o[s-u],c[s-u],e.position-(o[s]-o[s-u]),h),f=n.repeat(" ",t.indent)+l((e.line-u+1).toString(),d)+" | "+p.str+"\n"+f;for(p=a(e.buffer,o[s],c[s],e.position,h),f+=n.repeat(" ",t.indent)+l((e.line+1).toString(),d)+" | "+p.str+"\n",f+=n.repeat("-",t.indent+d+3+p.pos)+"^\n",u=1;u<=t.linesAfter&&!(s+u>=c.length);u++)p=a(e.buffer,o[s+u],c[s+u],e.position-(o[s]-o[s+u]),h),f+=n.repeat(" ",t.indent)+l((e.line+u+1).toString(),d)+" | "+p.str+"\n";return f.replace(/\n$/,"")},s=["kind","multi","resolve","construct","instanceOf","predicate","represent","representName","defaultStyle","styleAliases"],u=["scalar","sequence","mapping"];var p=function(e,t){if(t=t||{},Object.keys(t).forEach((function(t){if(-1===s.indexOf(t))throw new o('Unknown option "'+t+'" is met in definition of "'+e+'" YAML type.')})),this.options=t,this.tag=e,this.kind=t.kind||null,this.resolve=t.resolve||function(){return!0},this.construct=t.construct||function(e){return e},this.instanceOf=t.instanceOf||null,this.predicate=t.predicate||null,this.represent=t.represent||null,this.representName=t.representName||null,this.defaultStyle=t.defaultStyle||null,this.multi=t.multi||!1,this.styleAliases=function(e){var t={};return null!==e&&Object.keys(e).forEach((function(n){e[n].forEach((function(e){t[String(e)]=n}))})),t}(t.styleAliases||null),-1===u.indexOf(this.kind))throw new o('Unknown kind "'+this.kind+'" is specified for "'+e+'" YAML type.')};function f(e,t){var n=[];return e[t].forEach((function(e){var t=n.length;n.forEach((function(n,i){n.tag===e.tag&&n.kind===e.kind&&n.multi===e.multi&&(t=i)})),n[t]=e})),n}function d(e){return this.extend(e)}d.prototype.extend=function(e){var t=[],n=[];if(e instanceof p)n.push(e);else if(Array.isArray(e))n=n.concat(e);else{if(!e||!Array.isArray(e.implicit)&&!Array.isArray(e.explicit))throw new o("Schema.extend argument should be a Type, [ Type ], or a schema definition ({ implicit: [...], explicit: [...] })");e.implicit&&(t=t.concat(e.implicit)),e.explicit&&(n=n.concat(e.explicit))}t.forEach((function(e){if(!(e instanceof p))throw new o("Specified list of YAML types (or a single Type object) contains a non-Type object.");if(e.loadKind&&"scalar"!==e.loadKind)throw new o("There is a non-scalar type in the implicit list of a schema. Implicit resolving of such types is not supported.");if(e.multi)throw new o("There is a multi type in the implicit list of a schema. Multi tags can only be listed as explicit.")})),n.forEach((function(e){if(!(e instanceof p))throw new o("Specified list of YAML types (or a single Type object) contains a non-Type object.")}));var i=Object.create(d.prototype);return i.implicit=(this.implicit||[]).concat(t),i.explicit=(this.explicit||[]).concat(n),i.compiledImplicit=f(i,"implicit"),i.compiledExplicit=f(i,"explicit"),i.compiledTypeMap=function(){var e,t,n={scalar:{},sequence:{},mapping:{},fallback:{},multi:{scalar:[],sequence:[],mapping:[],fallback:[]}};function i(e){e.multi?(n.multi[e.kind].push(e),n.multi.fallback.push(e)):n[e.kind][e.tag]=n.fallback[e.tag]=e}for(e=0,t=arguments.length;e=0?"0b"+e.toString(2):"-0b"+e.toString(2).slice(1)},octal:function(e){return e>=0?"0o"+e.toString(8):"-0o"+e.toString(8).slice(1)},decimal:function(e){return e.toString(10)},hexadecimal:function(e){return e>=0?"0x"+e.toString(16).toUpperCase():"-0x"+e.toString(16).toUpperCase().slice(1)}},defaultStyle:"decimal",styleAliases:{binary:[2,"bin"],octal:[8,"oct"],decimal:[10,"dec"],hexadecimal:[16,"hex"]}}),x=new RegExp("^(?:[-+]?(?:[0-9][0-9_]*)(?:\\.[0-9_]*)?(?:[eE][-+]?[0-9]+)?|\\.[0-9_]+(?:[eE][-+]?[0-9]+)?|[-+]?\\.(?:inf|Inf|INF)|\\.(?:nan|NaN|NAN))$");var I=/^[-+]?[0-9]+e/;var S=new p("tag:yaml.org,2002:float",{kind:"scalar",resolve:function(e){return null!==e&&!(!x.test(e)||"_"===e[e.length-1])},construct:function(e){var t,n;return n="-"===(t=e.replace(/_/g,"").toLowerCase())[0]?-1:1,"+-".indexOf(t[0])>=0&&(t=t.slice(1)),".inf"===t?1===n?Number.POSITIVE_INFINITY:Number.NEGATIVE_INFINITY:".nan"===t?NaN:n*parseFloat(t,10)},predicate:function(e){return"[object Number]"===Object.prototype.toString.call(e)&&(e%1!=0||n.isNegativeZero(e))},represent:function(e,t){var i;if(isNaN(e))switch(t){case"lowercase":return".nan";case"uppercase":return".NAN";case"camelcase":return".NaN"}else if(Number.POSITIVE_INFINITY===e)switch(t){case"lowercase":return".inf";case"uppercase":return".INF";case"camelcase":return".Inf"}else if(Number.NEGATIVE_INFINITY===e)switch(t){case"lowercase":return"-.inf";case"uppercase":return"-.INF";case"camelcase":return"-.Inf"}else if(n.isNegativeZero(e))return"-0.0";return i=e.toString(10),I.test(i)?i.replace("e",".e"):i},defaultStyle:"lowercase"}),O=b.extend({implicit:[A,v,C,S]}),j=O,T=new RegExp("^([0-9][0-9][0-9][0-9])-([0-9][0-9])-([0-9][0-9])$"),N=new RegExp("^([0-9][0-9][0-9][0-9])-([0-9][0-9]?)-([0-9][0-9]?)(?:[Tt]|[ \\t]+)([0-9][0-9]?):([0-9][0-9]):([0-9][0-9])(?:\\.([0-9]*))?(?:[ \\t]*(Z|([-+])([0-9][0-9]?)(?::([0-9][0-9]))?))?$");var F=new p("tag:yaml.org,2002:timestamp",{kind:"scalar",resolve:function(e){return null!==e&&(null!==T.exec(e)||null!==N.exec(e))},construct:function(e){var t,n,i,r,o,a,l,c,s=0,u=null;if(null===(t=T.exec(e))&&(t=N.exec(e)),null===t)throw new Error("Date resolve error");if(n=+t[1],i=+t[2]-1,r=+t[3],!t[4])return new Date(Date.UTC(n,i,r));if(o=+t[4],a=+t[5],l=+t[6],t[7]){for(s=t[7].slice(0,3);s.length<3;)s+="0";s=+s}return t[9]&&(u=6e4*(60*+t[10]+ +(t[11]||0)),"-"===t[9]&&(u=-u)),c=new Date(Date.UTC(n,i,r,o,a,l,s)),u&&c.setTime(c.getTime()-u),c},instanceOf:Date,represent:function(e){return e.toISOString()}});var E=new p("tag:yaml.org,2002:merge",{kind:"scalar",resolve:function(e){return"<<"===e||null===e}}),M="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=\n\r";var L=new p("tag:yaml.org,2002:binary",{kind:"scalar",resolve:function(e){if(null===e)return!1;var t,n,i=0,r=e.length,o=M;for(n=0;n64)){if(t<0)return!1;i+=6}return i%8==0},construct:function(e){var t,n,i=e.replace(/[\r\n=]/g,""),r=i.length,o=M,a=0,l=[];for(t=0;t>16&255),l.push(a>>8&255),l.push(255&a)),a=a<<6|o.indexOf(i.charAt(t));return 0===(n=r%4*6)?(l.push(a>>16&255),l.push(a>>8&255),l.push(255&a)):18===n?(l.push(a>>10&255),l.push(a>>2&255)):12===n&&l.push(a>>4&255),new Uint8Array(l)},predicate:function(e){return"[object Uint8Array]"===Object.prototype.toString.call(e)},represent:function(e){var t,n,i="",r=0,o=e.length,a=M;for(t=0;t>18&63],i+=a[r>>12&63],i+=a[r>>6&63],i+=a[63&r]),r=(r<<8)+e[t];return 0===(n=o%3)?(i+=a[r>>18&63],i+=a[r>>12&63],i+=a[r>>6&63],i+=a[63&r]):2===n?(i+=a[r>>10&63],i+=a[r>>4&63],i+=a[r<<2&63],i+=a[64]):1===n&&(i+=a[r>>2&63],i+=a[r<<4&63],i+=a[64],i+=a[64]),i}}),_=Object.prototype.hasOwnProperty,D=Object.prototype.toString;var U=new p("tag:yaml.org,2002:omap",{kind:"sequence",resolve:function(e){if(null===e)return!0;var t,n,i,r,o,a=[],l=e;for(t=0,n=l.length;t>10),56320+(e-65536&1023))}for(var ie=new Array(256),re=new Array(256),oe=0;oe<256;oe++)ie[oe]=te(oe)?1:0,re[oe]=te(oe);function ae(e,t){this.input=e,this.filename=t.filename||null,this.schema=t.schema||K,this.onWarning=t.onWarning||null,this.legacy=t.legacy||!1,this.json=t.json||!1,this.listener=t.listener||null,this.implicitTypes=this.schema.compiledImplicit,this.typeMap=this.schema.compiledTypeMap,this.length=e.length,this.position=0,this.line=0,this.lineStart=0,this.lineIndent=0,this.firstTabInLine=-1,this.documents=[]}function le(e,t){var n={name:e.filename,buffer:e.input.slice(0,-1),position:e.position,line:e.line,column:e.position-e.lineStart};return n.snippet=c(n),new o(t,n)}function ce(e,t){throw le(e,t)}function se(e,t){e.onWarning&&e.onWarning.call(null,le(e,t))}var ue={YAML:function(e,t,n){var i,r,o;null!==e.version&&ce(e,"duplication of %YAML directive"),1!==n.length&&ce(e,"YAML directive accepts exactly one argument"),null===(i=/^([0-9]+)\.([0-9]+)$/.exec(n[0]))&&ce(e,"ill-formed argument of the YAML directive"),r=parseInt(i[1],10),o=parseInt(i[2],10),1!==r&&ce(e,"unacceptable YAML version of the document"),e.version=n[0],e.checkLineBreaks=o<2,1!==o&&2!==o&&se(e,"unsupported YAML version of the document")},TAG:function(e,t,n){var i,r;2!==n.length&&ce(e,"TAG directive accepts exactly two arguments"),i=n[0],r=n[1],G.test(i)||ce(e,"ill-formed tag handle (first argument) of the TAG directive"),P.call(e.tagMap,i)&&ce(e,'there is a previously declared suffix for "'+i+'" tag handle'),V.test(r)||ce(e,"ill-formed tag prefix (second argument) of the TAG directive");try{r=decodeURIComponent(r)}catch(t){ce(e,"tag prefix is malformed: "+r)}e.tagMap[i]=r}};function pe(e,t,n,i){var r,o,a,l;if(t1&&(e.result+=n.repeat("\n",t-1))}function be(e,t){var n,i,r=e.tag,o=e.anchor,a=[],l=!1;if(-1!==e.firstTabInLine)return!1;for(null!==e.anchor&&(e.anchorMap[e.anchor]=a),i=e.input.charCodeAt(e.position);0!==i&&(-1!==e.firstTabInLine&&(e.position=e.firstTabInLine,ce(e,"tab characters must not be used in indentation")),45===i)&&z(e.input.charCodeAt(e.position+1));)if(l=!0,e.position++,ge(e,!0,-1)&&e.lineIndent<=t)a.push(null),i=e.input.charCodeAt(e.position);else if(n=e.line,we(e,t,3,!1,!0),a.push(e.result),ge(e,!0,-1),i=e.input.charCodeAt(e.position),(e.line===n||e.lineIndent>t)&&0!==i)ce(e,"bad indentation of a sequence entry");else if(e.lineIndentt?g=1:e.lineIndent===t?g=0:e.lineIndentt?g=1:e.lineIndent===t?g=0:e.lineIndentt)&&(y&&(a=e.line,l=e.lineStart,c=e.position),we(e,t,4,!0,r)&&(y?g=e.result:m=e.result),y||(de(e,f,d,h,g,m,a,l,c),h=g=m=null),ge(e,!0,-1),s=e.input.charCodeAt(e.position)),(e.line===o||e.lineIndent>t)&&0!==s)ce(e,"bad indentation of a mapping entry");else if(e.lineIndent=0))break;0===o?ce(e,"bad explicit indentation width of a block scalar; it cannot be less than one"):u?ce(e,"repeat of an indentation width identifier"):(p=t+o-1,u=!0)}if(Q(a)){do{a=e.input.charCodeAt(++e.position)}while(Q(a));if(35===a)do{a=e.input.charCodeAt(++e.position)}while(!J(a)&&0!==a)}for(;0!==a;){for(he(e),e.lineIndent=0,a=e.input.charCodeAt(e.position);(!u||e.lineIndentp&&(p=e.lineIndent),J(a))f++;else{if(e.lineIndent0){for(r=a,o=0;r>0;r--)(a=ee(l=e.input.charCodeAt(++e.position)))>=0?o=(o<<4)+a:ce(e,"expected hexadecimal character");e.result+=ne(o),e.position++}else ce(e,"unknown escape sequence");n=i=e.position}else J(l)?(pe(e,n,i,!0),ye(e,ge(e,!1,t)),n=i=e.position):e.position===e.lineStart&&me(e)?ce(e,"unexpected end of the document within a double quoted scalar"):(e.position++,i=e.position)}ce(e,"unexpected end of the stream within a double quoted scalar")}(e,d)?y=!0:!function(e){var t,n,i;if(42!==(i=e.input.charCodeAt(e.position)))return!1;for(i=e.input.charCodeAt(++e.position),t=e.position;0!==i&&!z(i)&&!X(i);)i=e.input.charCodeAt(++e.position);return e.position===t&&ce(e,"name of an alias node must contain at least one character"),n=e.input.slice(t,e.position),P.call(e.anchorMap,n)||ce(e,'unidentified alias "'+n+'"'),e.result=e.anchorMap[n],ge(e,!0,-1),!0}(e)?function(e,t,n){var i,r,o,a,l,c,s,u,p=e.kind,f=e.result;if(z(u=e.input.charCodeAt(e.position))||X(u)||35===u||38===u||42===u||33===u||124===u||62===u||39===u||34===u||37===u||64===u||96===u)return!1;if((63===u||45===u)&&(z(i=e.input.charCodeAt(e.position+1))||n&&X(i)))return!1;for(e.kind="scalar",e.result="",r=o=e.position,a=!1;0!==u;){if(58===u){if(z(i=e.input.charCodeAt(e.position+1))||n&&X(i))break}else if(35===u){if(z(e.input.charCodeAt(e.position-1)))break}else{if(e.position===e.lineStart&&me(e)||n&&X(u))break;if(J(u)){if(l=e.line,c=e.lineStart,s=e.lineIndent,ge(e,!1,-1),e.lineIndent>=t){a=!0,u=e.input.charCodeAt(e.position);continue}e.position=o,e.line=l,e.lineStart=c,e.lineIndent=s;break}}a&&(pe(e,r,o,!1),ye(e,e.line-l),r=o=e.position,a=!1),Q(u)||(o=e.position+1),u=e.input.charCodeAt(++e.position)}return pe(e,r,o,!1),!!e.result||(e.kind=p,e.result=f,!1)}(e,d,1===i)&&(y=!0,null===e.tag&&(e.tag="?")):(y=!0,null===e.tag&&null===e.anchor||ce(e,"alias node should not have any properties")),null!==e.anchor&&(e.anchorMap[e.anchor]=e.result)):0===g&&(y=c&&be(e,h))),null===e.tag)null!==e.anchor&&(e.anchorMap[e.anchor]=e.result);else if("?"===e.tag){for(null!==e.result&&"scalar"!==e.kind&&ce(e,'unacceptable node kind for ! tag; it should be "scalar", not "'+e.kind+'"'),s=0,u=e.implicitTypes.length;s"),null!==e.result&&f.kind!==e.kind&&ce(e,"unacceptable node kind for !<"+e.tag+'> tag; it should be "'+f.kind+'", not "'+e.kind+'"'),f.resolve(e.result,e.tag)?(e.result=f.construct(e.result,e.tag),null!==e.anchor&&(e.anchorMap[e.anchor]=e.result)):ce(e,"cannot resolve a node with !<"+e.tag+"> explicit tag")}return null!==e.listener&&e.listener("close",e),null!==e.tag||null!==e.anchor||y}function ke(e){var t,n,i,r,o=e.position,a=!1;for(e.version=null,e.checkLineBreaks=e.legacy,e.tagMap=Object.create(null),e.anchorMap=Object.create(null);0!==(r=e.input.charCodeAt(e.position))&&(ge(e,!0,-1),r=e.input.charCodeAt(e.position),!(e.lineIndent>0||37!==r));){for(a=!0,r=e.input.charCodeAt(++e.position),t=e.position;0!==r&&!z(r);)r=e.input.charCodeAt(++e.position);for(i=[],(n=e.input.slice(t,e.position)).length<1&&ce(e,"directive name must not be less than one character in length");0!==r;){for(;Q(r);)r=e.input.charCodeAt(++e.position);if(35===r){do{r=e.input.charCodeAt(++e.position)}while(0!==r&&!J(r));break}if(J(r))break;for(t=e.position;0!==r&&!z(r);)r=e.input.charCodeAt(++e.position);i.push(e.input.slice(t,e.position))}0!==r&&he(e),P.call(ue,n)?ue[n](e,n,i):se(e,'unknown document directive "'+n+'"')}ge(e,!0,-1),0===e.lineIndent&&45===e.input.charCodeAt(e.position)&&45===e.input.charCodeAt(e.position+1)&&45===e.input.charCodeAt(e.position+2)?(e.position+=3,ge(e,!0,-1)):a&&ce(e,"directives end mark is expected"),we(e,e.lineIndent-1,4,!1,!0),ge(e,!0,-1),e.checkLineBreaks&&H.test(e.input.slice(o,e.position))&&se(e,"non-ASCII line breaks are interpreted as content"),e.documents.push(e.result),e.position===e.lineStart&&me(e)?46===e.input.charCodeAt(e.position)&&(e.position+=3,ge(e,!0,-1)):e.position=55296&&i<=56319&&t+1=56320&&n<=57343?1024*(i-55296)+n-56320+65536:i}function Re(e){return/^\n* /.test(e)}function Be(e,t,n,i,r,o,a,l){var c,s,u=0,p=null,f=!1,d=!1,h=-1!==i,g=-1,m=De(s=Ye(e,0))&&s!==Oe&&!_e(s)&&45!==s&&63!==s&&58!==s&&44!==s&&91!==s&&93!==s&&123!==s&&125!==s&&35!==s&&38!==s&&42!==s&&33!==s&&124!==s&&61!==s&&62!==s&&39!==s&&34!==s&&37!==s&&64!==s&&96!==s&&function(e){return!_e(e)&&58!==e}(Ye(e,e.length-1));if(t||a)for(c=0;c=65536?c+=2:c++){if(!De(u=Ye(e,c)))return 5;m=m&&qe(u,p,l),p=u}else{for(c=0;c=65536?c+=2:c++){if(10===(u=Ye(e,c)))f=!0,h&&(d=d||c-g-1>i&&" "!==e[g+1],g=c);else if(!De(u))return 5;m=m&&qe(u,p,l),p=u}d=d||h&&c-g-1>i&&" "!==e[g+1]}return f||d?n>9&&Re(e)?5:a?2===o?5:2:d?4:3:!m||a||r(e)?2===o?5:2:1}function Ke(e,t,n,i,r){e.dump=function(){if(0===t.length)return 2===e.quotingType?'""':"''";if(!e.noCompatMode&&(-1!==Te.indexOf(t)||Ne.test(t)))return 2===e.quotingType?'"'+t+'"':"'"+t+"'";var a=e.indent*Math.max(1,n),l=-1===e.lineWidth?-1:Math.max(Math.min(e.lineWidth,40),e.lineWidth-a),c=i||e.flowLevel>-1&&n>=e.flowLevel;switch(Be(t,c,e.indent,l,(function(t){return function(e,t){var n,i;for(n=0,i=e.implicitTypes.length;n"+Pe(t,e.indent)+We(Me(function(e,t){var n,i,r=/(\n+)([^\n]*)/g,o=(l=e.indexOf("\n"),l=-1!==l?l:e.length,r.lastIndex=l,He(e.slice(0,l),t)),a="\n"===e[0]||" "===e[0];var l;for(;i=r.exec(e);){var c=i[1],s=i[2];n=" "===s[0],o+=c+(a||n||""===s?"":"\n")+He(s,t),a=n}return o}(t,l),a));case 5:return'"'+function(e){for(var t,n="",i=0,r=0;r=65536?r+=2:r++)i=Ye(e,r),!(t=je[i])&&De(i)?(n+=e[r],i>=65536&&(n+=e[r+1])):n+=t||Fe(i);return n}(t)+'"';default:throw new o("impossible error: invalid scalar style")}}()}function Pe(e,t){var n=Re(e)?String(t):"",i="\n"===e[e.length-1];return n+(i&&("\n"===e[e.length-2]||"\n"===e)?"+":i?"":"-")+"\n"}function We(e){return"\n"===e[e.length-1]?e.slice(0,-1):e}function He(e,t){if(""===e||" "===e[0])return e;for(var n,i,r=/ [^ ]/g,o=0,a=0,l=0,c="";n=r.exec(e);)(l=n.index)-o>t&&(i=a>o?a:l,c+="\n"+e.slice(o,i),o=i+1),a=l;return c+="\n",e.length-o>t&&a>o?c+=e.slice(o,a)+"\n"+e.slice(a+1):c+=e.slice(o),c.slice(1)}function $e(e,t,n,i){var r,o,a,l="",c=e.tag;for(r=0,o=n.length;r tag resolver accepts not "'+s+'" style');i=c.represent[s](t,s)}e.dump=i}return!0}return!1}function Ve(e,t,n,i,r,a,l){e.tag=null,e.dump=n,Ge(e,n,!1)||Ge(e,n,!0);var c,s=Ie.call(e.dump),u=i;i&&(i=e.flowLevel<0||e.flowLevel>t);var p,f,d="[object Object]"===s||"[object Array]"===s;if(d&&(f=-1!==(p=e.duplicates.indexOf(n))),(null!==e.tag&&"?"!==e.tag||f||2!==e.indent&&t>0)&&(r=!1),f&&e.usedDuplicates[p])e.dump="*ref_"+p;else{if(d&&f&&!e.usedDuplicates[p]&&(e.usedDuplicates[p]=!0),"[object Object]"===s)i&&0!==Object.keys(e.dump).length?(!function(e,t,n,i){var r,a,l,c,s,u,p="",f=e.tag,d=Object.keys(n);if(!0===e.sortKeys)d.sort();else if("function"==typeof e.sortKeys)d.sort(e.sortKeys);else if(e.sortKeys)throw new o("sortKeys must be a boolean or a function");for(r=0,a=d.length;r1024)&&(e.dump&&10===e.dump.charCodeAt(0)?u+="?":u+="? "),u+=e.dump,s&&(u+=Le(e,t)),Ve(e,t+1,c,!0,s)&&(e.dump&&10===e.dump.charCodeAt(0)?u+=":":u+=": ",p+=u+=e.dump));e.tag=f,e.dump=p||"{}"}(e,t,e.dump,r),f&&(e.dump="&ref_"+p+e.dump)):(!function(e,t,n){var i,r,o,a,l,c="",s=e.tag,u=Object.keys(n);for(i=0,r=u.length;i1024&&(l+="? "),l+=e.dump+(e.condenseFlow?'"':"")+":"+(e.condenseFlow?"":" "),Ve(e,t,a,!1,!1)&&(c+=l+=e.dump));e.tag=s,e.dump="{"+c+"}"}(e,t,e.dump),f&&(e.dump="&ref_"+p+" "+e.dump));else if("[object Array]"===s)i&&0!==e.dump.length?(e.noArrayIndent&&!l&&t>0?$e(e,t-1,e.dump,r):$e(e,t,e.dump,r),f&&(e.dump="&ref_"+p+e.dump)):(!function(e,t,n){var i,r,o,a="",l=e.tag;for(i=0,r=n.length;i",e.dump=c+" "+e.dump)}return!0}function Ze(e,t){var n,i,r=[],o=[];for(Je(e,r,o),n=0,i=o.length;ni.keyCol)return e.skipToEnd(),"string";if(i.literal&&(i.literal=!1),e.sol()){if(i.keyCol=0,i.pair=!1,i.pairStart=!1,e.match("---"))return"def";if(e.match("..."))return"def";if(e.match(/\s*-\s+/))return"meta"}if(e.match(/^(\{|\}|\[|\])/))return"{"==t?i.inlinePairs++:"}"==t?i.inlinePairs--:"["==t?i.inlineList++:i.inlineList--,"meta";if(0)\s*/))return i.literal=!0,"meta";if(e.match(/^\s*(\&|\*)[a-z0-9\._-]+\b/i))return"variable-2";if(0==i.inlinePairs&&e.match(/^\s*-?[0-9\.\,]+\s?$/))return"number";if(0'"%@`][^\s'":]|[^\s,\[\]{}#&*!|>'"%@`])[^#:]*(?=:($|\s))/)?(i.pair=!0,i.keyCol=e.indentation(),"atom"):i.pair&&e.match(/^:\s*/)?(i.pairStart=!0,"meta"):(i.pairStart=!1,i.escaped="\\"==t,e.next(),null)},startState:function(){return{pair:!1,pairStart:!1,keyCol:0,inlinePairs:0,inlineList:0,literal:!1,escaped:!1}},lineComment:"#",fold:"indent"}}),e.defineMIME("text/x-yaml","yaml"),e.defineMIME("text/yaml","yaml")}); \ No newline at end of file diff --git a/luci-app-mosdns/htdocs/luci-static/resources/codemirror5/theme/dracula.min.css b/luci-app-mosdns/htdocs/luci-static/resources/codemirror5/theme/dracula.min.css new file mode 100644 index 000000000..bb5a8ecdd --- /dev/null +++ b/luci-app-mosdns/htdocs/luci-static/resources/codemirror5/theme/dracula.min.css @@ -0,0 +1 @@ +.cm-s-dracula .CodeMirror-gutters,.cm-s-dracula.CodeMirror{background-color:#282a36!important;color:#f8f8f2!important;border:none}.cm-s-dracula .CodeMirror-gutters{color:#282a36}.cm-s-dracula .CodeMirror-cursor{border-left:solid thin #f8f8f0}.cm-s-dracula .CodeMirror-linenumber{color:#6d8a88}.cm-s-dracula .CodeMirror-selected{background:rgba(255,255,255,.1)}.cm-s-dracula .CodeMirror-line::selection,.cm-s-dracula .CodeMirror-line>span::selection,.cm-s-dracula .CodeMirror-line>span>span::selection{background:rgba(255,255,255,.1)}.cm-s-dracula .CodeMirror-line::-moz-selection,.cm-s-dracula .CodeMirror-line>span::-moz-selection,.cm-s-dracula .CodeMirror-line>span>span::-moz-selection{background:rgba(255,255,255,.1)}.cm-s-dracula span.cm-comment{color:#6272a4}.cm-s-dracula span.cm-string,.cm-s-dracula span.cm-string-2{color:#f1fa8c}.cm-s-dracula span.cm-number{color:#bd93f9}.cm-s-dracula span.cm-variable{color:#50fa7b}.cm-s-dracula span.cm-variable-2{color:#fff}.cm-s-dracula span.cm-def{color:#50fa7b}.cm-s-dracula span.cm-operator{color:#ff79c6}.cm-s-dracula span.cm-keyword{color:#ff79c6}.cm-s-dracula span.cm-atom{color:#bd93f9}.cm-s-dracula span.cm-meta{color:#f8f8f2}.cm-s-dracula span.cm-tag{color:#ff79c6}.cm-s-dracula span.cm-attribute{color:#50fa7b}.cm-s-dracula span.cm-qualifier{color:#50fa7b}.cm-s-dracula span.cm-property{color:#66d9ef}.cm-s-dracula span.cm-builtin{color:#50fa7b}.cm-s-dracula span.cm-type,.cm-s-dracula span.cm-variable-3{color:#ffb86c}.cm-s-dracula .CodeMirror-activeline-background{background:rgba(255,255,255,.1)}.cm-s-dracula .CodeMirror-matchingbracket{text-decoration:underline;color:#fff!important} \ No newline at end of file diff --git a/luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js b/luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js new file mode 100644 index 000000000..436fabe49 --- /dev/null +++ b/luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js @@ -0,0 +1,457 @@ +'use strict'; +'require form'; +'require fs'; +'require poll'; +'require rpc'; +'require uci'; +'require ui'; +'require view'; + +var callServiceList = rpc.declare({ + object: 'service', + method: 'list', + params: ['name'], + expect: { '': {} } +}); + +function getServiceStatus() { + return L.resolveDefault(callServiceList('mosdns'), {}).then(function (res) { + var isRunning = false; + try { + isRunning = res['mosdns']['instances']['mosdns']['running']; + } catch (e) { } + return isRunning; + }); +} + +function renderStatus(isRunning) { + var spanTemp = '%s %s'; + var renderHTML; + if (isRunning) { + renderHTML = spanTemp.format('green', _('MosDNS'), _('RUNNING')); + } else { + renderHTML = spanTemp.format('red', _('MosDNS'), _('NOT RUNNING')); + } + + return renderHTML; +} + +async function loadCodeMirrorResources() { + const styles = [ + '/luci-static/resources/codemirror5/theme/dracula.min.css', + '/luci-static/resources/codemirror5/addon/lint/lint.min.css', + '/luci-static/resources/codemirror5/codemirror.min.css', + ]; + const scripts = [ + '/luci-static/resources/codemirror5/libs/js-yaml.min.js', + '/luci-static/resources/codemirror5/codemirror.min.js', + '/luci-static/resources/codemirror5/addon/display/autorefresh.min.js', + '/luci-static/resources/codemirror5/mode/yaml/yaml.min.js', + '/luci-static/resources/codemirror5/addon/lint/lint.min.js', + '/luci-static/resources/codemirror5/addon/lint/yaml-lint.min.js', + ]; + const loadStyles = async () => { + for (const href of styles) { + const link = document.createElement('link'); + link.rel = 'stylesheet'; + link.href = href; + document.head.appendChild(link); + } + }; + const loadScripts = async () => { + for (const src of scripts) { + const script = document.createElement('script'); + script.src = src; + document.head.appendChild(script); + await new Promise(resolve => script.onload = resolve); + } + }; + await loadStyles(); + await loadScripts(); +} + +return view.extend({ + load: function () { + return Promise.all([ + L.resolveDefault(fs.exec('/usr/bin/mosdns', ['version']), null), + ]); + }, + + handleFlushCache: function (m, section_id, ev) { + return fs.exec('/usr/share/mosdns/mosdns.sh', ['flush']) + .then(function (lazy_cache) { + var res = lazy_cache.code; + if (res === 0) { + ui.addNotification(null, E('p', _('Flushing DNS Cache Success.')), 'info'); + } else { + ui.addNotification(null, E('p', _('Flushing DNS Cache Failed, Please check if MosDNS is running.')), 'error'); + } + }); + }, + + render: function (basic) { + var m, s, o, v; + v = ''; + + if (basic[0] && basic[0].code === 0) { + v = basic[0].stdout.trim(); + } + m = new form.Map('mosdns', _('MosDNS') + ' ' + v, + _('MosDNS is a plugin-based DNS forwarder/traffic splitter.')); + + s = m.section(form.TypedSection); + s.anonymous = true; + s.render = function () { + setTimeout(function () { + poll.add(function () { + return L.resolveDefault(getServiceStatus()).then(function (res) { + var view = document.getElementById('service_status'); + if (view) { + view.innerHTML = renderStatus(res); + } else { + console.error('Element #service_status not found.'); + } + }); + }); + }, 100); + + /* dynamically loading Codemirror resources */ + loadCodeMirrorResources(); + + return E('div', { class: 'cbi-section', id: 'status_bar' }, [ + E('p', { id: 'service_status' }, _('Collecting data...')) + ]); + } + + s = m.section(form.NamedSection, 'config', 'mosdns'); + + s.tab('basic', _('Basic Options')); + s.tab("advanced", _("Advanced Options")); + s.tab("cloudflare", _("Cloudflare Options")); + s.tab("api", _("API Options")); + s.tab('geodata', _('GeoData Export')); + + /* basic */ + o = s.taboption('basic', form.Flag, 'enabled', _('Enabled')); + o.default = o.disabled; + o.rmempty = false; + + o = s.taboption('basic', form.ListValue, 'configfile', _('Config File')); + o.value('/var/etc/mosdns.json', _('Default Config')); + o.value('/etc/mosdns/config_custom.yaml', _('Custom Config')); + o.default = '/var/etc/mosdns.json'; + + o = s.taboption('basic', form.Value, 'listen_port', _('Listen port')); + o.default = '5335'; + o.datatype = 'port'; + o.depends('configfile', '/var/etc/mosdns.json'); + + o = s.taboption('basic', form.ListValue, 'log_level', _('Log Level')); + o.value('debug', _('Debug')); + o.value('info', _('Info')); + o.value('warn', _('Warning')); + o.value('error', _('Error')); + o.default = 'info'; + o.depends('configfile', '/var/etc/mosdns.json'); + + o = s.taboption('basic', form.Value, 'log_file', _('Log File')); + o.placeholder = '/var/log/mosdns.log'; + o.default = '/var/log/mosdns.log'; + o.depends('configfile', '/var/etc/mosdns.json'); + + o = s.taboption('basic', form.Flag, 'redirect', _('DNS Forward'), _('Forward Dnsmasq Domain Name resolution requests to MosDNS')); + o.default = false; + + if (L.hasSystemFeature('firewall4')) { + o = s.taboption('basic', form.Flag, 'local_dns_redirect', _('DNS redirect'), _('Force redirect all local DNS queries to MosDNS, a.k.a. DNS Hijacking')); + o.default = false; + o.depends('redirect', '1'); + } + + o = s.taboption('basic', form.Flag, 'prefer_ipv4_cn', _('China DNS prefer IPv4'), + _('IPv4 is preferred for China DNS resolution of dual-stack addresses, and is not affected when the destination is IPv6 only')); + o.depends('configfile', '/var/etc/mosdns.json'); + o.default = false; + + o = s.taboption('basic', form.Flag, 'prefer_ipv4', _('Remote DNS prefer IPv4'), + _('IPv4 is preferred for Remote / Streaming Media DNS resolution of dual-stack addresses, and is not affected when the destination is IPv6 only')); + o.depends('configfile', '/var/etc/mosdns.json'); + o.default = false; + + o = s.taboption('basic', form.Flag, 'custom_local_dns', _('Custom China DNS'), _('Follow WAN interface DNS if not enabled')); + o.depends('configfile', '/var/etc/mosdns.json'); + o.default = false; + + o = s.taboption('basic', form.Flag, 'apple_optimization', _('Apple domains optimization'), + _('For Apple domains equipped with Chinese mainland CDN, always responsive to Chinese CDN IP addresses')); + o.depends('custom_local_dns', '1'); + o.default = false; + + o = s.taboption('basic', form.DynamicList, 'local_dns', _('China DNS server')); + o.value('119.29.29.29', _('Tencent Public DNS (119.29.29.29)')); + o.value('119.28.28.28', _('Tencent Public DNS (119.28.28.28)')); + o.value('223.5.5.5', _('Aliyun Public DNS (223.5.5.5)')); + o.value('223.6.6.6', _('Aliyun Public DNS (223.6.6.6)')); + o.value('180.184.1.1', _('TrafficRoute Public DNS (180.184.1.1)')); + o.value('180.184.2.2', _('TrafficRoute Public DNS (180.184.2.2)')); + o.value('114.114.114.114', _('Xinfeng Public DNS (114.114.114.114)')); + o.value('114.114.115.115', _('Xinfeng Public DNS (114.114.115.115)')); + o.value('180.76.76.76', _('Baidu Public DNS (180.76.76.76)')); + o.value('https://doh.pub/dns-query', _('Tencent Public DNS (DNS over HTTPS)')); + o.value('quic://dns.alidns.com', _('Aliyun Public DNS (DNS over QUIC)')); + o.value('https://dns.alidns.com/dns-query', _('Aliyun Public DNS (DNS over HTTPS)')); + o.value('h3://dns.alidns.com/dns-query', _('Aliyun Public DNS (DNS over HTTPS/3)')); + o.value('https://doh.360.cn/dns-query', _('360 Public DNS (DNS over HTTPS)')); + o.default = '119.29.29.29'; + o.depends('custom_local_dns', '1'); + + o = s.taboption('basic', form.DynamicList, 'remote_dns', _('Remote DNS server')); + o.value('tls://1.1.1.1', _('CloudFlare Public DNS (1.1.1.1)')); + o.value('tls://1.0.0.1', _('CloudFlare Public DNS (1.0.0.1)')); + o.value('tls://8.8.8.8', _('Google Public DNS (8.8.8.8)')); + o.value('tls://8.8.4.4', _('Google Public DNS (8.8.4.4)')); + o.value('tls://9.9.9.9', _('Quad9 Public DNS (9.9.9.9)')); + o.value('tls://149.112.112.112', _('Quad9 Public DNS (149.112.112.112)')); + o.value('tls://208.67.222.222', _('Cisco Public DNS (208.67.222.222)')); + o.value('tls://208.67.220.220', _('Cisco Public DNS (208.67.220.220)')); + o.default = 'tls://8.8.8.8'; + o.depends('configfile', '/var/etc/mosdns.json'); + + o = s.taboption('basic', form.Flag, 'custom_stream_media_dns', _('Custom Stream Media DNS'), + _('Netflix, Disney+, Hulu and streaming media rules list will use this DNS')); + o.depends('configfile', '/var/etc/mosdns.json'); + o.default = false; + + o = s.taboption('basic', form.DynamicList, 'stream_media_dns', _('Streaming Media DNS server')); + o.value('tls://1.1.1.1', _('CloudFlare Public DNS (1.1.1.1)')); + o.value('tls://1.0.0.1', _('CloudFlare Public DNS (1.0.0.1)')); + o.value('tls://8.8.8.8', _('Google Public DNS (8.8.8.8)')); + o.value('tls://8.8.4.4', _('Google Public DNS (8.8.4.4)')); + o.value('tls://9.9.9.9', _('Quad9 Public DNS (9.9.9.9)')); + o.value('tls://149.112.112.112', _('Quad9 Public DNS (149.112.112.112)')); + o.value('tls://208.67.222.222', _('Cisco Public DNS (208.67.222.222)')); + o.value('tls://208.67.220.220', _('Cisco Public DNS (208.67.220.220)')); + o.default = 'tls://8.8.8.8'; + o.depends('custom_stream_media_dns', '1'); + + o = s.taboption('basic', form.Value, 'bootstrap_dns', _('Bootstrap DNS servers'), + _('Bootstrap DNS servers are used to resolve IP addresses of the DoH/DoT resolvers you specify as upstreams')); + o.value('119.29.29.29', _('Tencent Public DNS (119.29.29.29)')); + o.value('119.28.28.28', _('Tencent Public DNS (119.28.28.28)')); + o.value('223.5.5.5', _('Aliyun Public DNS (223.5.5.5)')); + o.value('223.6.6.6', _('Aliyun Public DNS (223.6.6.6)')); + o.value('114.114.114.114', _('Xinfeng Public DNS (114.114.114.114)')); + o.value('114.114.115.115', _('Xinfeng Public DNS (114.114.115.115)')); + o.value('180.76.76.76', _('Baidu Public DNS (180.76.76.76)')); + o.value('8.8.8.8', _('Google Public DNS (8.8.8.8)')); + o.value('1.1.1.1', _('CloudFlare Public DNS (1.1.1.1)')); + o.default = '119.29.29.29'; + o.depends('configfile', '/var/etc/mosdns.json'); + + /* advanced */ + o = s.taboption('advanced', form.Value, 'concurrent', _('Concurrent'), + _('DNS query request concurrency, The number of upstream DNS servers that are allowed to initiate requests at the same time')); + o.datatype = 'and(uinteger,min(1),max(3))'; + o.default = '2'; + o.depends('configfile', '/var/etc/mosdns.json'); + + o = s.taboption('advanced', form.Value, 'idle_timeout', _('Idle Timeout'), + _('DoH/TCP/DoT Connection Multiplexing idle timeout (default 30 seconds)')) + o.datatype = 'and(uinteger,min(1))'; + o.default = '30'; + o.depends('configfile', '/var/etc/mosdns.json'); + + o = s.taboption('advanced', form.Flag, 'enable_pipeline', _('TCP/DoT Connection Multiplexing'), + _('Enable TCP/DoT RFC 7766 new Query Pipelining connection multiplexing mode')) + o.rmempty = false; + o.default = false; + o.depends('configfile', '/var/etc/mosdns.json'); + + o = s.taboption('advanced', form.Flag, 'insecure_skip_verify', _('Disable TLS Certificate'), + _('Disable TLS Servers certificate validation, Can be useful if system CA certificate expires or the system time is out of order')); + o.rmempty = false; + o.default = false; + o.depends('configfile', '/var/etc/mosdns.json'); + + o = s.taboption('advanced', form.Flag, 'enable_ecs_remote', + _('Enable EDNS client subnet')); + o.rmempty = false; + o.default = false; + o.depends('configfile', '/var/etc/mosdns.json'); + + o = s.taboption('advanced', form.Value, 'remote_ecs_ip', _('IP Address'), + _('Please provide the IP address you use when accessing foreign websites. This IP subnet (0/24) will be used as the ECS address for Remote / Streaming Media DNS requests') + + _('This feature is typically used when using a self-built DNS server as an Remote / Streaming Media DNS upstream (requires support from the upstream server)')); + o.datatype = 'ipaddr'; + o.depends('enable_ecs_remote', '1'); + + o = s.taboption('advanced', form.Flag, 'dns_leak', _('Prevent DNS Leaks'), + _('Enable this option fallback policy forces forwarding to remote DNS')); + o.rmempty = false; + o.default = false; + o.depends('configfile', '/var/etc/mosdns.json'); + + o = s.taboption('advanced', form.Flag, 'cache', _('Enable DNS Cache')); + o.rmempty = false; + o.default = false; + o.depends('configfile', '/var/etc/mosdns.json'); + + o = s.taboption('advanced', form.Value, 'cache_size', _('DNS Cache Size')); + o.datatype = 'and(uinteger,min(0))'; + o.default = 8000; + o.depends('cache', '1'); + + o = s.taboption('advanced', form.Value, 'lazy_cache_ttl', _('Lazy Cache TTL'), + _('Lazy cache survival time (in second). To disable Lazy Cache, please set to 0.')); + o.datatype = 'and(uinteger,min(0))'; + o.default = 86400; + o.depends('cache', '1'); + + o = s.taboption('advanced', form.Flag, 'dump_file', _('Cache Dump'), + _('Save the cache locally and reload the cache dump on the next startup')); + o.rmempty = false; + o.default = false; + o.depends('cache', '1'); + + o = s.taboption('advanced', form.Value, 'dump_interval', + _('Auto Save Cache Interval')); + o.datatype = 'and(uinteger,min(0))'; + o.default = 3600; + o.depends('dump_file', '1'); + + o = s.taboption('advanced', form.Value, 'minimal_ttl', _('Minimum TTL'), + _('Modify the Minimum TTL value (seconds) for DNS answer results, 0 indicating no modification')); + o.datatype = 'and(uinteger,min(0),max(604800))'; + o.default = 0; + o.depends('configfile', '/var/etc/mosdns.json'); + + o = s.taboption('advanced', form.Value, 'maximum_ttl', _('Maximum TTL'), + _('Modify the Maximum TTL value (seconds) for DNS answer results, 0 indicating no modification')); + o.datatype = 'and(uinteger,min(0),max(604800))'; + o.default = 0; + o.depends('configfile', '/var/etc/mosdns.json'); + + o = s.taboption('advanced', form.Flag, 'reject_type65', _('Disable RR Type 65 (HTTPS/SVCB)'), + _('Block DNS RR Type 65 records (HTTPS/SVCB, used for HTTP/3, ECH, etc.), force using only A/AAAA records.')); + o.default = 0; + o.depends('configfile', '/var/etc/mosdns.json'); + + o = s.taboption('advanced', form.Flag, 'adblock', _('Enable DNS ADblock')); + o.depends('configfile', '/var/etc/mosdns.json'); + o.default = false; + + o = s.taboption('advanced', form.DynamicList, 'ad_source', _('ADblock Source'), + _('When using custom rule sources, please use rule types supported by MosDNS (domain lists).') + + '
' + + _('Support for local files, such as: file:///var/mosdns/example.txt')); + o.depends('adblock', '1'); + o.default = 'geosite.dat'; + o.value('geosite.dat', 'v2ray-geosite'); + o.value('https://raw.githubusercontent.com/privacy-protection-tools/anti-AD/master/anti-ad-domains.txt', 'anti-AD') + o.value('https://raw.githubusercontent.com/Cats-Team/AdRules/main/mosdns_adrules.txt', 'Cats-Team/AdRules') + o.value('https://raw.githubusercontent.com/neodevpro/neodevhost/master/domain', 'NEO DEV HOST') + + /* cloudflare */ + o = s.taboption('cloudflare', form.Flag, 'cloudflare', _('Enabled'), + _('Match the parsing result with the Cloudflare IP ranges, and when there is a successful match, \ + use the \'Custom IP\' as the parsing result (experimental feature)')); + o.rmempty = false; + o.default = false; + o.depends('configfile', '/var/etc/mosdns.json'); + + o = s.taboption('cloudflare', form.DynamicList, 'cloudflare_ip', _('Custom IP')); + o.datatype = 'ipaddr'; + o.depends('configfile', '/var/etc/mosdns.json'); + + o = s.taboption('cloudflare', form.TextValue, '_cloudflare', + _('Cloudflare IP Ranges'), + _('IPv4 CIDR: https://www.cloudflare.com/ips-v4
IPv6 CIDR: https://www.cloudflare.com/ips-v6')); + o.rows = 15; + o.depends('configfile', '/var/etc/mosdns.json'); + o.cfgvalue = function (section_id) { + return fs.trimmed('/etc/mosdns/rule/cloudflare-cidr.txt'); + }; + o.write = function (section_id, formvalue) { + return this.cfgvalue(section_id).then(function (value) { + if (value == formvalue) { + return; + } + return fs.write('/etc/mosdns/rule/cloudflare-cidr.txt', formvalue.trim().replace(/\r\n/g, '\n') + '\n') + .then(function (i) { + return fs.exec('/etc/init.d/mosdns', ['restart']); + }); + }); + }; + + /* api */ + o = s.taboption('api', form.Value, 'listen_port_api', _('API Listen port')); + o.datatype = 'and(port,min(1))'; + o.default = 9091; + o.depends('configfile', '/var/etc/mosdns.json'); + + o = s.taboption('api', form.Button, '_flush_cache', null, + _('Flushing DNS Cache will clear any IP addresses or DNS records from MosDNS cache.')); + o.title = ' '; + o.inputtitle = _('Flush DNS Cache'); + o.inputstyle = 'apply'; + o.onclick = L.bind(this.handleFlushCache, this, m); + o.depends('cache', '1'); + + /* configuration */ + var configeditor = null; + setTimeout(function () { + var textarea = document.getElementById('widget.cbid.mosdns.config._custom'); + if (textarea) { + configeditor = CodeMirror.fromTextArea(textarea, { + autoRefresh: true, + lineNumbers: true, + lineWrapping: true, + lint: true, + gutters: ['CodeMirror-lint-markers'], + matchBrackets: true, + mode: "text/yaml", + styleActiveLine: true, + theme: "dracula" + }); + } + }, 600); + o = s.taboption('basic', form.TextValue, '_custom', _('Configuration Editor'), + _('This is the content of the file \'/etc/mosdns/config_custom.yaml\' from which your MosDNS configuration will be generated. \ + Only accepts configuration content in yaml format.')); + o.rows = 25; + o.depends('configfile', '/etc/mosdns/config_custom.yaml'); + o.cfgvalue = function (section_id) { + return fs.trimmed('/etc/mosdns/config_custom.yaml'); + }; + o.write = function (section_id, formvalue) { + if (configeditor) { + var editorContent = configeditor.getValue(); + if (editorContent === formvalue) { + return; + } + return fs.write('/etc/mosdns/config_custom.yaml', editorContent.trim().replace(/\r\n/g, '\n') + '\n') + .then(function (i) { + return fs.exec('/etc/init.d/mosdns', ['restart']); + }) + .then(function () { + return window.location.reload(); + }) + .catch(function (e) { + ui.addNotification(null, E('p', _('Unable to save contents: %s').format(e.message))); + }); + } + }; + + o = s.taboption('geodata', form.DynamicList, 'geosite_tags', _('GeoSite Tags'), + _('Enter the GeoSite.dat category to be exported, Allow add multiple tags'), + _('Export directory: /var/mosdns')); + o.depends('configfile', '/etc/mosdns/config_custom.yaml'); + + o = s.taboption('geodata', form.DynamicList, 'geoip_tags', _('GeoIP Tags'), + _('Enter the GeoIP.dat category to be exported, Allow add multiple tags'), + _('Export directory: /var/mosdns')); + o.depends('configfile', '/etc/mosdns/config_custom.yaml'); + + return m.render(); + } +}); diff --git a/luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/logs.js b/luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/logs.js new file mode 100644 index 000000000..48ba3d7fa --- /dev/null +++ b/luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/logs.js @@ -0,0 +1,70 @@ +'use strict'; +'require dom'; +'require fs'; +'require poll'; +'require view'; + +var scrollPosition = 0; +var userScrolled = false; +var logTextarea; + +function pollLog() { + return Promise.all([ + fs.exec_direct('/usr/share/mosdns/mosdns.sh', ['printlog']).then(function (res) { + return res.trim().split(/\n/).join('\n'); + }), + ]).then(function (data) { + logTextarea.value = data[0] || _('No log data.'); + + if (!userScrolled) { + logTextarea.scrollTop = logTextarea.scrollHeight; + } else { + logTextarea.scrollTop = scrollPosition; + } + }); +}; + +return view.extend({ + handleCleanLogs: function () { + return fs.exec('/usr/share/mosdns/mosdns.sh', ['cleanlog']) + .catch(function (e) { ui.addNotification(null, E('p', e.message)) }); + }, + + render: function () { + logTextarea = E('textarea', { + 'class': 'cbi-input-textarea', + 'wrap': 'off', + 'readonly': 'readonly', + 'style': 'width: calc(100% - 20px);height: 535px;margin: 10px;overflow-y: scroll;', + }); + + logTextarea.addEventListener('scroll', function () { + userScrolled = true; + scrollPosition = logTextarea.scrollTop; + }); + + var log_textarea_wrapper = E('div', { 'id': 'log_textarea' }, logTextarea); + + poll.add(pollLog); + + var clear_logs_button = E('input', { 'class': 'btn cbi-button-action', 'type': 'button', 'style': 'margin-left: 10px; margin-top: 10px;', 'value': _('Clear logs') }); + clear_logs_button.addEventListener('click', this.handleCleanLogs.bind(this)); + + return E([ + E('div', { 'class': 'cbi-map' }, [ + E('h2', { 'name': 'content' }, '%s - %s'.format(_('MosDNS'), _('Log Data'))), + E('div', { 'class': 'cbi-section' }, [ + clear_logs_button, + log_textarea_wrapper, + E('div', { 'style': 'text-align:right' }, + E('small', {}, _('Refresh every %s seconds.').format(L.env.pollinterval)) + ) + ]) + ]) + ]); + }, + + handleSave: null, + handleSaveApply: null, + handleReset: null +}); diff --git a/luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/rules.js b/luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/rules.js new file mode 100644 index 000000000..ad6774c63 --- /dev/null +++ b/luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/rules.js @@ -0,0 +1,234 @@ +'use strict'; +'require form'; +'require fs'; +'require ui'; +'require view'; + +return view.extend({ + render: function () { + var m, s, o; + + m = new form.Map("mosdns", _("Rule Settings"), + _('The list of rules only apply to \'Default Config\' profiles.')); + + s = m.section(form.TypedSection); + s.anonymous = true; + s.sortable = true; + + s.tab('whitelist', _('White Lists')); + s.tab('blocklist', _('Block Lists')); + s.tab('greylist', _('Grey Lists')); + s.tab('ddnslist', _('DDNS Lists')); + s.tab('hostslist', _('Hosts')); + s.tab('redirectlist', _('Redirect')); + s.tab('localptrlist', _('Block PTR')); + s.tab('streamingmedialist', _('Streaming Media')); + + o = s.taboption('whitelist', form.TextValue, '_whitelist', + null, + '' + + _('Added domain names always permit resolution using \'local DNS\' with the highest priority (one domain per line, supports domain matching rules).') + + '' + ); + o.rows = 25; + o.cfgvalue = function (section_id) { + return fs.trimmed('/etc/mosdns/rule/whitelist.txt').catch(function (e) { + return ""; + }); + }; + o.write = function (section_id, formvalue) { + return this.cfgvalue(section_id).then(function (value) { + if (value == formvalue) { + return; + } + return fs.write('/etc/mosdns/rule/whitelist.txt', formvalue.trim().replace(/\r\n/g, '\n') + '\n') + .catch(function (e) { + ui.addNotification(null, E('p', _('Unable to save contents: %s').format(e.message))); + }); + }); + }; + + o = s.taboption('blocklist', form.TextValue, '_blocklist', + null, + '' + + _('Added domain names will block DNS resolution (one domain per line, supports domain matching rules).') + + '' + ); + o.rows = 25; + o.cfgvalue = function (section_id) { + return fs.trimmed('/etc/mosdns/rule/blocklist.txt').catch(function (e) { + return ""; + }); + }; + o.write = function (section_id, formvalue) { + return this.cfgvalue(section_id).then(function (value) { + if (value == formvalue) { + return; + } + return fs.write('/etc/mosdns/rule/blocklist.txt', formvalue.trim().replace(/\r\n/g, '\n') + '\n') + .catch(function (e) { + ui.addNotification(null, E('p', _('Unable to save contents: %s').format(e.message))); + }); + }); + }; + + o = s.taboption('greylist', form.TextValue, '_greylist', + null, + '' + + _('Added domain names will always use \'Remote DNS\' for resolution (one domain per line, supports domain matching rules).') + + '' + ); + o.rows = 25; + o.cfgvalue = function (section_id) { + return fs.trimmed('/etc/mosdns/rule/greylist.txt').catch(function (e) { + return ""; + }); + }; + o.write = function (section_id, formvalue) { + return this.cfgvalue(section_id).then(function (value) { + if (value == formvalue) { + return; + } + return fs.write('/etc/mosdns/rule/greylist.txt', formvalue.trim().replace(/\r\n/g, '\n') + '\n') + .catch(function (e) { + ui.addNotification(null, E('p', _('Unable to save contents: %s').format(e.message))); + }); + }); + }; + + o = s.taboption('ddnslist', form.TextValue, '_ddnslist', + null, + '' + + _('Added domain names will always use \'Local DNS\' for resolution, with a forced TTL of 5 seconds, and results will not be cached (one domain per line, supports domain matching rules).') + + '' + ); + o.rows = 25; + o.cfgvalue = function (section_id) { + return fs.trimmed('/etc/mosdns/rule/ddnslist.txt').catch(function (e) { + return ""; + }); + }; + o.write = function (section_id, formvalue) { + return this.cfgvalue(section_id).then(function (value) { + if (value == formvalue) { + return; + } + return fs.write('/etc/mosdns/rule/ddnslist.txt', formvalue.trim().replace(/\r\n/g, '\n') + '\n') + .catch(function (e) { + ui.addNotification(null, E('p', _('Unable to save contents: %s').format(e.message))); + }); + }); + }; + + o = s.taboption('hostslist', form.TextValue, '_hostslist', + null, + '' + + _('Custom Hosts rewrite, for example: baidu.com 10.0.0.1 (one rule per line, supports domain matching rules).') + + '' + ); + o.rows = 25; + o.cfgvalue = function (section_id) { + return fs.trimmed('/etc/mosdns/rule/hosts.txt').catch(function (e) { + return ""; + }); + }; + o.write = function (section_id, formvalue) { + return this.cfgvalue(section_id).then(function (value) { + if (value == formvalue) { + return; + } + return fs.write('/etc/mosdns/rule/hosts.txt', formvalue.trim().replace(/\r\n/g, '\n') + '\n') + .catch(function (e) { + ui.addNotification(null, E('p', _('Unable to save contents: %s').format(e.message))); + }); + }); + }; + + o = s.taboption('redirectlist', form.TextValue, '_redirectlist', + null, + '' + + _('Redirecting requests for domain names. Request domain A, but return records for domain B, for example: baidu.com qq.com (one rule per line).') + + '' + ); + o.rows = 25; + o.cfgvalue = function (section_id) { + return fs.trimmed('/etc/mosdns/rule/redirect.txt').catch(function (e) { + return ""; + }); + }; + o.write = function (section_id, formvalue) { + return this.cfgvalue(section_id).then(function (value) { + if (value == formvalue) { + return; + } + return fs.write('/etc/mosdns/rule/redirect.txt', formvalue.trim().replace(/\r\n/g, '\n') + '\n') + .catch(function (e) { + ui.addNotification(null, E('p', _('Unable to save contents: %s').format(e.message))); + }); + }); + }; + + o = s.taboption('localptrlist', form.TextValue, '_localptrlist', + null, + '' + + _('Added domain names will block PTR requests (one domain per line, supports domain matching rules).') + + '' + ); + o.rows = 25; + o.cfgvalue = function (section_id) { + return fs.trimmed('/etc/mosdns/rule/local-ptr.txt').catch(function (e) { + return ""; + }); + }; + o.write = function (section_id, formvalue) { + return this.cfgvalue(section_id).then(function (value) { + if (value == formvalue) { + return; + } + return fs.write('/etc/mosdns/rule/local-ptr.txt', formvalue.trim().replace(/\r\n/g, '\n') + '\n') + .catch(function (e) { + ui.addNotification(null, E('p', _('Unable to save contents: %s').format(e.message))); + }); + }); + }; + + o = s.taboption('streamingmedialist', form.TextValue, '_streamingmedialist', + null, + '' + + _('When enabling \'Custom Stream Media DNS\', added domains will always use the \'Streaming Media DNS server\' for resolution (one domain per line, supports domain matching rules).') + + '' + ); + o.rows = 25; + o.cfgvalue = function (section_id) { + return fs.trimmed('/etc/mosdns/rule/streaming.txt').catch(function (e) { + return ""; + }); + }; + o.write = function (section_id, formvalue) { + return this.cfgvalue(section_id).then(function (value) { + if (value == formvalue) { + return; + } + return fs.write('/etc/mosdns/rule/streaming.txt', formvalue.trim().replace(/\r\n/g, '\n') + '\n') + .catch(function (e) { + ui.addNotification(null, E('p', _('Unable to save contents: %s').format(e.message))); + }); + }); + }; + + return m.render(); + }, + + handleSaveApply: function (ev) { + var m = this.map; + onclick = L.bind(this.handleSave, this, m); + return fs.exec('/etc/init.d/mosdns', ['restart']) + .then(function () { + window.location.reload(); + }) + .catch(function (e) { + ui.addNotification(null, E('p', _('Failed to restart mosdns: %s').format(e.message))); + }); + }, + handleReset: null +}); diff --git a/luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/update.js b/luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/update.js new file mode 100644 index 000000000..2dd12dba4 --- /dev/null +++ b/luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/update.js @@ -0,0 +1,75 @@ +'use strict'; +'require form'; +'require fs'; +'require ui'; +'require view'; + +return view.extend({ + handleUpdate: function (m, section_id, ev) { + return fs.exec('/usr/share/mosdns/mosdns.sh', ['geodata']) + .then(function (i) { + var res = i.code; + if (res === 0) { + ui.addNotification(null, E('p', _('Update success')), 'info'); + } else { + ui.addNotification(null, E('p', i.stderr + '
' + i.stdout), 'warn'); + ui.addNotification(null, E('p', _('Update failed, Please check the network status')), 'error'); + } + }); + }, + + render: function () { + var m, s, o; + + m = new form.Map('mosdns', _('Update GeoIP & GeoSite databases'), + _('Automatically update GeoIP and GeoSite databases as well as ad filtering rules through scheduled tasks.')); + + s = m.section(form.TypedSection); + s.anonymous = true; + + o = s.option(form.Flag, 'geo_auto_update', _('Enable Auto Database Update')); + o.rmempty = false; + + o = s.option(form.ListValue, 'geo_update_week_time', _('Update Cycle')); + o.value('*', _('Every Day')); + o.value('1', _('Every Monday')); + o.value('2', _('Every Tuesday')); + o.value('3', _('Every Wednesday')); + o.value('4', _('Every Thursday')); + o.value('5', _('Every Friday')); + o.value('6', _('Every Saturday')); + o.value('0', _('Every Sunday')); + o.default = 3; + + o = s.option(form.ListValue, 'geo_update_day_time', _('Update Time')); + for (let t = 0; t < 24; t++) { + o.value(t, t + ':00'); + }; + o.default = 3; + + o = s.option(form.ListValue, 'geoip_type', _('GeoIP Type'), + _('Little: only include Mainland China and Private IP addresses.') + + '
' + + _('Full: includes all Countries and Private IP addresses.') + ); + o.value('geoip', _('Full')); + o.value('geoip-only-cn-private', _('Little')); + o.rmempty = false; + o.default = 'geoip'; + + o = s.option(form.Value, 'github_proxy', _('GitHub Proxy'), + _('Update data files with GitHub Proxy, leave blank to disable proxy downloads.')); + o.value('https://gh-proxy.com', _('https://gh-proxy.com')); + o.rmempty = true; + o.default = ''; + + o = s.option(form.Button, '_udpate', null, + _('Check And Update GeoData.')); + o.title = _('Database Update'); + o.inputtitle = _('Check And Update'); + o.inputstyle = 'apply'; + o.onclick = L.bind(this.handleUpdate, this, m); + + return m.render(); + } +}); diff --git a/luci-app-mosdns/po/zh-cn b/luci-app-mosdns/po/zh-cn new file mode 120000 index 000000000..8d69574dd --- /dev/null +++ b/luci-app-mosdns/po/zh-cn @@ -0,0 +1 @@ +zh_Hans \ No newline at end of file diff --git a/luci-app-mosdns/po/zh_Hans/mosdns.po b/luci-app-mosdns/po/zh_Hans/mosdns.po new file mode 100644 index 000000000..812dbb36c --- /dev/null +++ b/luci-app-mosdns/po/zh_Hans/mosdns.po @@ -0,0 +1,486 @@ +msgid "" +msgstr "" +"Content-Type: text/plain; charset=UTF-8\n" +"Project-Id-Version: PACKAGE VERSION\n" +"Last-Translator: Automatically generated\n" +"Language-Team: none\n" +"Language: zh_Hans\n" +"MIME-Version: 1.0\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "MosDNS" +msgstr "MosDNS" + +msgid "MosDNS is a plugin-based DNS forwarder/traffic splitter." +msgstr "MosDNS 是一个插件化的 DNS 转发/分流器。" + +msgid "Basic Setting" +msgstr "基本设置" + +msgid "Basic Options" +msgstr "基本选项" + +msgid "Advanced Options" +msgstr "高级选项" + +msgid "Cloudflare Options" +msgstr "Cloudflare 选项" + +msgid "API Options" +msgstr "API 选项" + +msgid "RUNNING" +msgstr "运行中" + +msgid "NOT RUNNING" +msgstr "未运行" + +msgid "Collecting data..." +msgstr "获取数据中..." + +msgid "Enabled" +msgstr "启用" + +msgid "Listen port" +msgstr "监听端口" + +msgid "API Listen port" +msgstr "API 监听端口" + +msgid "Flush DNS Cache" +msgstr "刷新 DNS 缓存" + +msgid "Flushing DNS Cache will clear any IP addresses or DNS records from MosDNS cache." +msgstr "刷新 DNS 缓存会清空 MosDNS 所有 IP 地址和 DNS 解析缓存。" + +msgid "Flushing DNS Cache Success." +msgstr "刷新 DNS 缓存成功" + +msgid "Flushing DNS Cache Failed, Please check if MosDNS is running." +msgstr "刷新 DNS 缓存失败,请检查 MosDNS 状态是否在运行中。" + +msgid "Match the parsing result with the Cloudflare IP ranges, and when there is a successful match, use the 'Custom IP' as the parsing result (experimental feature)" +msgstr "将解析结果与 Cloudflare IP 范围进行匹配,当匹配成功时,使用 “自选 IP” 作为解析结果(实验性功能)" + +msgid "Custom IP" +msgstr "自选 IP" + +msgid "Cloudflare IP Ranges" +msgstr "Cloudflare IP 范围" + +msgid "Log Level" +msgstr "日志等级" + +msgid "DNS Forward" +msgstr "DNS 转发" + +msgid "Forward Dnsmasq Domain Name resolution requests to MosDNS" +msgstr "将 Dnsmasq 域名解析请求转发到 MosDNS 服务器" + +msgid "DNS redirect" +msgstr "DNS 重定向" + +msgid "Force redirect all local DNS queries to MosDNS, a.k.a. DNS Hijacking" +msgstr "强制将所有本地 DNS 查询重定向到 MosDNS,即 DNS 劫持。" + +msgid "Disable RR Type 65 (HTTPS/SVCB)" +msgstr "禁用 RR Type 65 (HTTPS/SVCB)" + +msgid "Block DNS RR Type 65 records (HTTPS/SVCB, used for HTTP/3, ECH, etc.), force using only A/AAAA records." +msgstr "屏蔽 DNS 类型 65 记录(HTTPS/SVCB,用于 HTTP/3、ECH 等),强制仅使用 A/AAAA 解析。" + +msgid "Enable DNS ADblock" +msgstr "启用 DNS 广告过滤" + +msgid "ADblock Source" +msgstr "广告过滤规则来源" + +msgid "When using custom rule sources, please use rule types supported by MosDNS (domain lists)." +msgstr "使用自定义规则来源时,请使用 MosDNS 支持的规则类型(域名列表)" + +msgid "Support for local files, such as: file:///var/mosdns/example.txt" +msgstr "支持本地文件,例如:file:///var/mosdns/example.txt" + +msgid "Configuration Editor" +msgstr "配置编辑器" + +msgid "Edit the MosDNS custom configuration file." +msgstr "编辑 MosDNS 自定义配置文件。" + +msgid "Configuration have been saved." +msgstr "配置已保存。" + +msgid "This is the content of the file '/etc/mosdns/config_custom.yaml' from which your MosDNS configuration will be generated. Only accepts configuration content in yaml format." +msgstr "这是文件 “/etc/mosdns/config_custom.yaml” 的内容,您的 MosDNS 配置将从此文件生成。仅接受 yaml 格式的配置内容。" + +msgid "Geodata Update" +msgstr "更新数据库" + +msgid "Update GeoIP & GeoSite databases" +msgstr "更新广告规则、GeoIP & GeoSite 数据库" + +msgid "Automatically update GeoIP and GeoSite databases as well as ad filtering rules through scheduled tasks." +msgstr "通过定时任务自动更新 GeoIP 和 GeoSite 数据库以及广告过滤规则。" + +msgid "Update Time" +msgstr "更新时间" + +msgid "Update Cycle" +msgstr "更新周期" + +msgid "Every Day" +msgstr "每天" + +msgid "Every Monday" +msgstr "每周一" + +msgid "Every Tuesday" +msgstr "每周二" + +msgid "Every Wednesday" +msgstr "每周三" + +msgid "Every Thursday" +msgstr "每周四" + +msgid "Every Friday" +msgstr "每周五" + +msgid "Every Saturday" +msgstr "每周六" + +msgid "Every Sunday" +msgstr "每周日" + +msgid "GeoIP Type" +msgstr "GeoIP 类型" + +msgid "Little" +msgstr "轻量" + +msgid "Little: only include Mainland China and Private IP addresses." +msgstr "轻量:仅包含中国大陆和私有 IP 地址。" + +msgid "Full" +msgstr "全量" + +msgid "Full: includes all Countries and Private IP addresses." +msgstr "全量:包含所有国家和私有 IP 地址。" + +msgid "GitHub Proxy" +msgstr "GitHub 代理" + +msgid "Update data files with GitHub Proxy, leave blank to disable proxy downloads." +msgstr "通过 GitHub 代理更新数据文件,留空则禁用代理下载。" + +msgid "Database Update" +msgstr "数据库更新" + +msgid "Check And Update GeoData." +msgstr "检查并更新 GeoData 数据库。" + +msgid "Check And Update" +msgstr "检查并更新" + +msgid "Enable Auto Database Update" +msgstr "启用自动更新" + +msgid "Update success" +msgstr "更新成功" + +msgid "Update failed, Please check the network status" +msgstr "更新失败,请检查网络状态" + +msgid "Config File" +msgstr "配置文件" + +msgid "Default Config" +msgstr "内置预设" + +msgid "Custom Config" +msgstr "自定义" + +msgid "Log File" +msgstr "日志文件" + +msgid "China DNS prefer IPv4" +msgstr "国内 DNS 首选 IPv4" + +msgid "IPv4 is preferred for China DNS resolution of dual-stack addresses, and is not affected when the destination is IPv6 only" +msgstr "国内 DNS 解析双栈地址时首选 IPv4,目标仅 IPv6 时不受影响" + +msgid "Remote DNS prefer IPv4" +msgstr "远程 DNS 首选 IPv4" + +msgid "IPv4 is preferred for Remote / Streaming Media DNS resolution of dual-stack addresses, and is not affected when the destination is IPv6 only" +msgstr "远程 / 流媒体 DNS 解析双栈地址时首选 IPv4,目标仅 IPv6 时不受影响" + +msgid "Custom China DNS" +msgstr "自定义国内 DNS" + +msgid "Follow WAN interface DNS if not enabled" +msgstr "不启用则使用 WAN 接口 DNS" + +msgid "Apple domains optimization" +msgstr "Apple 域名解析优化" + +msgid "For Apple domains equipped with Chinese mainland CDN, always responsive to Chinese CDN IP addresses" +msgstr "配备中国大陆 CDN 的 Apple 域名,始终应答中国大陆 CDN 地址" + +msgid "China DNS server" +msgstr "国内 DNS 服务器" + +msgid "Remote DNS server" +msgstr "远程 DNS 服务器" + +msgid "Bootstrap DNS servers" +msgstr "Bootstrap DNS 服务器" + +msgid "Bootstrap DNS servers are used to resolve IP addresses of the DoH/DoT resolvers you specify as upstreams" +msgstr "Bootstrap DNS 服务器用于解析您指定为上游的 DoH / DoT 解析器的 IP 地址" + +msgid "Tencent Public DNS (119.29.29.29)" +msgstr "腾讯公共 DNS(119.29.29.29)" + +msgid "Tencent Public DNS (119.28.28.28)" +msgstr "腾讯公共 DNS(119.28.28.28)" + +msgid "Aliyun Public DNS (223.5.5.5)" +msgstr "阿里云公共 DNS(223.5.5.5)" + +msgid "Aliyun Public DNS (223.6.6.6)" +msgstr "阿里云公共 DNS(223.6.6.6)" + +msgid "TrafficRoute Public DNS (180.184.1.1)" +msgstr "火山引擎公共 DNS(180.184.1.1)" + +msgid "TrafficRoute Public DNS (180.184.2.2)" +msgstr "火山引擎公共 DNS(180.184.2.2)" + +msgid "Xinfeng Public DNS (114.114.114.114)" +msgstr "信风公共 DNS(114.114.114.114)" + +msgid "Xinfeng Public DNS (114.114.115.115)" +msgstr "信风公共 DNS(114.114.115.115)" + +msgid "Baidu Public DNS (180.76.76.76)" +msgstr "百度公共 DNS(180.76.76.76)" + +msgid "Tencent Public DNS (DNS over HTTPS)" +msgstr "腾讯公共 DNS(DNS over HTTPS)" + +msgid "Aliyun Public DNS (DNS over QUIC)" +msgstr "阿里云公共 DNS(DNS over QUIC)" + +msgid "Aliyun Public DNS (DNS over HTTPS)" +msgstr "阿里云公共 DNS(DNS over HTTPS)" + +msgid "Aliyun Public DNS (DNS over HTTPS/3)" +msgstr "阿里云公共 DNS(DNS over HTTPS/3)" + +msgid "360 Public DNS (DNS over HTTPS)" +msgstr "360 安全 DNS(DNS over HTTPS)" + +msgid "CloudFlare Public DNS (1.1.1.1)" +msgstr "CloudFlare 公共 DNS(1.1.1.1)" + +msgid "CloudFlare Public DNS (1.0.0.1)" +msgstr "CloudFlare 公共 DNS(1.0.0.1)" + +msgid "Google Public DNS (8.8.8.8)" +msgstr "谷歌公共 DNS(8.8.8.8)" + +msgid "Google Public DNS (8.8.4.4)" +msgstr "谷歌公共 DNS(8.8.4.4)" + +msgid "Quad9 Public DNS (9.9.9.9)" +msgstr "Quad9 公共 DNS(9.9.9.9)" + +msgid "Quad9 Public DNS (149.112.112.112)" +msgstr "Quad9 公共 DNS(149.112.112.112)" + +msgid "Cisco Public DNS (208.67.222.222)" +msgstr "思科公共 DNS(208.67.222.222)" + +msgid "Cisco Public DNS (208.67.220.220)" +msgstr "思科公共 DNS(208.67.220.220)" + +msgid "Concurrent" +msgstr "DNS 服务器并发数(默认 2)" + +msgid "DNS query request concurrency, The number of upstream DNS servers that are allowed to initiate requests at the same time" +msgstr "DNS 查询请求并发数,允许同时发起请求的上游 DNS 服务器数量" + +msgid "Idle Timeout" +msgstr "空闲超时" + +msgid "DoH/TCP/DoT Connection Multiplexing idle timeout (default 30 seconds)" +msgstr "DoH/TCP/DoT 连接复用空闲保持时间(默认 30 秒)" + +msgid "TCP/DoT Connection Multiplexing" +msgstr "TCP/DoT 连接复用" + +msgid "Enable TCP/DoT RFC 7766 new Query Pipelining connection multiplexing mode" +msgstr "启用 TCP/DoT RFC 7766 新型 Query Pipelining 连接复用模式" + +msgid "Disable TLS Certificate" +msgstr "禁用 TLS 证书" + +msgid "Disable TLS Servers certificate validation, Can be useful if system CA certificate expires or the system time is out of order" +msgstr "禁用 TLS 服务器证书验证,当系统 CA 证书过期或系统时间错乱时,本选项可能会有用" + +msgid "Enable EDNS client subnet" +msgstr "启用 EDNS 客户端子网" + +msgid "IP Address" +msgstr "IP 地址" + +msgid "Please provide the IP address you use when accessing foreign websites. This IP subnet (0/24) will be used as the ECS address for Remote / Streaming Media DNS requests" +msgstr "请提供您在访问国外网站时使用的 IP 地址,这个 IP 子网(0/24)将用作 远程 / 流媒体 DNS 请求的 ECS 地址" + +msgid "This feature is typically used when using a self-built DNS server as an Remote / Streaming Media DNS upstream (requires support from the upstream server)" +msgstr "此功能通常在使用自建 DNS 服务器作为 远程 / 流媒体 DNS 上游时使用(需要上游服务器的支持)" + +msgid "Prevent DNS Leaks" +msgstr "防止 DNS 泄漏" + +msgid "Enable this option fallback policy forces forwarding to remote DNS" +msgstr "启用此选项 fallback 策略会强制转发到远程 DNS" + +msgid "Enable DNS Cache" +msgstr "启用 DNS 缓存" + +msgid "DNS Cache Size" +msgstr "DNS 缓存大小" + +msgid "DNS cache size (in piece)." +msgstr "DNS 缓存大小(单位:条)" + +msgid "Lazy Cache TTL" +msgstr "乐观缓存 TTL" + +msgid "Lazy cache survival time (in second). To disable Lazy Cache, please set to 0." +msgstr "乐观缓存生存时间(单位:秒),要禁用乐观缓存,请设置为 0" + +msgid "Cache Dump" +msgstr "自动保存缓存" + +msgid "Save the cache locally and reload the cache dump on the next startup" +msgstr "保存缓存到本地文件,以供下次启动时重新载入使用" + +msgid "Auto Save Cache Interval" +msgstr "自动保存缓存间隔(秒)" + +msgid "Minimum TTL" +msgstr "覆盖最小 TTL 值(默认 0)" + +msgid "Modify the Minimum TTL value (seconds) for DNS answer results, 0 indicating no modification" +msgstr "修改 DNS 应答结果的最小 TTL 值 (秒),0 表示不修改" + +msgid "Maximum TTL" +msgstr "覆盖最大 TTL 值(默认 0)" + +msgid "Modify the Maximum TTL value (seconds) for DNS answer results, 0 indicating no modification" +msgstr "修改 DNS 应答结果的最大 TTL 值(秒),0 表示不修改" + +msgid "Logs" +msgstr "日志" + +msgid "Clear logs" +msgstr "清空日志" + +msgid "Log Data" +msgstr "日志数据" + +msgid "No log data." +msgstr "无日志数据。" + +msgid "Refresh every %s seconds." +msgstr "每 %s 秒刷新。" + +msgid "Rules" +msgstr "规则列表" + +msgid "Rule Settings" +msgstr "自定义规则列表" + +msgid "Failed to restart mosdns: %s" +msgstr "无法重启 MosDNS:%s" + +msgid "Unable to save contents: %s" +msgstr "无法保存内容:%s" + +msgid "The list of rules only apply to 'Default Config' profiles." +msgstr "规则列表仅适用于 “内置预设” 配置文件。" + +msgid "White Lists" +msgstr "白名单" + +msgid "Added domain names always permit resolution using 'local DNS' with the highest priority (one domain per line, supports domain matching rules)." +msgstr "加入的域名始终允许使用 “本地 DNS” 进行解析,且优先级最高(每个域名一行,支持域名匹配规则)" + +msgid "Block Lists" +msgstr "黑名单" + +msgid "Added domain names will block DNS resolution (one domain per line, supports domain matching rules)." +msgstr "加入的域名将屏蔽 DNS 解析(每个域名一行,支持域名匹配规则)" + +msgid "Grey Lists" +msgstr "灰名单" + +msgid "Added domain names will always use 'Remote DNS' for resolution (one domain per line, supports domain matching rules)." +msgstr "加入的域名始终使用 “远程 DNS” 进行解析(每个域名一行,支持域名匹配规则)" + +msgid "DDNS Lists" +msgstr "DDNS 域名" + +msgid "Added domain names will always use 'Local DNS' for resolution, with a forced TTL of 5 seconds, and results will not be cached (one domain per line, supports domain matching rules)." +msgstr "加入的域名始终使用 “本地 DNS” 进行解析,并且强制 TTL 5 秒,解析结果不会进入缓存(每个域名一行,支持域名匹配规则)" + +msgid "Custom Hosts rewrite, for example: baidu.com 10.0.0.1 (one rule per line, supports domain matching rules)." +msgstr "自定义 Hosts 重写,如:baidu.com 10.0.0.1(每个规则一行,支持域名匹配规则)" + +msgid "Redirect" +msgstr "重定向" + +msgid "Redirecting requests for domain names. Request domain A, but return records for domain B, for example: baidu.com qq.com (one rule per line)." +msgstr "重定向请求的域名。请求域名 A,但返回域名 B 的记录,如:baidu.com qq.com(每个规则一行)" + +msgid "Block PTR" +msgstr "PTR 黑名单" + +msgid "Added domain names will block PTR requests (one domain per line, supports domain matching rules)." +msgstr "加入的域名将阻止 PTR 请求(每个域名一行,支持域名匹配规则)" + +msgid "GeoData Export" +msgstr "GeoData 导出" + +msgid "GeoSite Tags" +msgstr "GeoSite 标签" + +msgid "Enter the GeoSite.dat category to be exported, Allow add multiple tags" +msgstr "填写需要导出的 GeoSite.dat 类别条目,允许添加多个标签" + +msgid "GeoIP Tags" +msgstr "GeoIP 标签" + +msgid "Enter the GeoIP.dat category to be exported, Allow add multiple tags" +msgstr "输入需要导出的 GeoIP.dat 类别条目,允许添加多个标签" + +msgid "Export directory: /var/mosdns" +msgstr "导出目录:/var/mosdns" + +msgid "Custom Stream Media DNS" +msgstr "自定义流媒体 DNS" + +msgid "Streaming Media DNS server" +msgstr "流媒体 DNS 服务器" + +msgid "Netflix, Disney+, Hulu and streaming media rules list will use this DNS" +msgstr "自定义 Netflix、Disney+、Hulu 以及 “流媒体” 规则列表的 DNS 服务器" + +msgid "Streaming Media" +msgstr "流媒体" + +msgid "When enabling 'Custom Stream Media DNS', added domains will always use the 'Streaming Media DNS server' for resolution (one domain per line, supports domain matching rules)." +msgstr "启用 “自定义流媒体 DNS” 时,加入的域名始终使用 “流媒体 DNS 服务器” 进行解析(每个域名一行,支持域名匹配规则)" diff --git a/luci-app-mosdns/root/etc/config/mosdns b/luci-app-mosdns/root/etc/config/mosdns new file mode 100644 index 000000000..a236958c3 --- /dev/null +++ b/luci-app-mosdns/root/etc/config/mosdns @@ -0,0 +1,30 @@ + +config mosdns 'config' + option enabled '0' + option listen_port '5335' + option geo_auto_update '0' + option geo_update_week_time '*' + option geo_update_day_time '2' + option configfile '/var/etc/mosdns.json' + option log_level 'info' + option log_file '/var/log/mosdns.log' + option cache '1' + option concurrent '2' + option idle_timeout '30' + option minimal_ttl '0' + option maximum_ttl '0' + option enable_pipeline '1' + option insecure_skip_verify '0' + option dns_leak '0' + option cloudflare '0' + option listen_port_api '9091' + option bootstrap_dns '119.29.29.29' + list remote_dns 'tls://8.8.8.8' + option redirect '1' + option prefer_ipv4 '1' + option enable_ecs_remote '0' + option cache_size '8000' + option lazy_cache_ttl '86400' + option dump_file '0' + option reject_type65 '1' + diff --git a/luci-app-mosdns/root/etc/hotplug.d/iface/99-mosdns b/luci-app-mosdns/root/etc/hotplug.d/iface/99-mosdns new file mode 100755 index 000000000..1fb646fb0 --- /dev/null +++ b/luci-app-mosdns/root/etc/hotplug.d/iface/99-mosdns @@ -0,0 +1,2 @@ +#!/bin/sh +[ "$ACTION" = ifup ] && /etc/init.d/mosdns restart diff --git a/luci-app-mosdns/root/etc/init.d/mosdns b/luci-app-mosdns/root/etc/init.d/mosdns new file mode 100755 index 000000000..1d72fe584 --- /dev/null +++ b/luci-app-mosdns/root/etc/init.d/mosdns @@ -0,0 +1,808 @@ +#!/bin/sh /etc/rc.common +# Copyright (C) 2020-2022, IrineSistiana +# Copyright (C) 2023-2024, sbwml + +START=75 +USE_PROCD=1 + +PROG=/usr/bin/mosdns +CONF=$(uci -q get mosdns.config.configfile) +CRON_FILE=/etc/crontabs/root +DUMP_FILE=/etc/mosdns/cache.dump +DUMP_FILE_DEFAULT=/usr/share/mosdns/cache.dump +MOSDNS_SCRIPT=/usr/share/mosdns/mosdns.sh + +get_config() { + config_get enabled $1 enabled 0 + config_get adblock $1 adblock 0 + config_get ad_source $1 ad_source "" + config_get cache $1 cache 0 + config_get cache_size $1 cache_size 8000 + config_get lazy_cache_ttl $1 lazy_cache_ttl 86400 + config_get dump_file $1 dump_file 0 + config_get dump_interval $1 dump_interval 3600 + config_get enable_pipeline $1 enable_pipeline 0 + config_get geo_auto_update $1 geo_auto_update 0 + config_get geo_update_day_time $1 geo_update_day_time 2 + config_get geo_update_week_time $1 geo_update_week_time "*" + config_get listen_port $1 listen_port 5335 + config_get log_file $1 log_file "/var/log/mosdns.log" + config_get log_level $1 log_level "info" + config_get minimal_ttl $1 minimal_ttl 0 + config_get maximum_ttl $1 maximum_ttl 0 + config_get redirect $1 redirect 0 + config_get local_dns_redirect $1 local_dns_redirect 0 + config_get prefer_ipv4_cn $1 prefer_ipv4_cn 0 + config_get prefer_ipv4 $1 prefer_ipv4 0 + config_get remote_dns $1 remote_dns "tls://8.8.8.8 tls://1.1.1.1" + config_get custom_local_dns $1 custom_local_dns 0 + config_get apple_optimization $1 apple_optimization 0 + config_get custom_stream_media_dns $1 custom_stream_media_dns 0 + config_get stream_media_dns $1 stream_media_dns "tls://8.8.8.8" + config_get bootstrap_dns $1 bootstrap_dns "119.29.29.29" + config_get listen_port_api $1 listen_port_api 9091 + config_get concurrent $1 concurrent 1 + config_get insecure_skip_verify $1 insecure_skip_verify 0 + config_get idle_timeout $1 idle_timeout 30 + config_get enable_ecs_remote $1 enable_ecs_remote 0 + config_get remote_ecs_ip $1 remote_ecs_ip "110.34.181.1" + config_get dns_leak $1 dns_leak 0 + config_get cloudflare $1 cloudflare 0 + config_get cloudflare_ip $1 cloudflare_ip "" + config_get reject_type65 $1 reject_type65 "0" +} + +generate_config() { + # jshn shell library + . /usr/share/libubox/jshn.sh + # json data + json_init + # log + json_add_object 'log' + json_add_string "level" "$log_level" + json_add_string "file" "$log_file" + json_close_object + # api + json_add_object 'api' + json_add_string "http" "0.0.0.0:$listen_port_api" + json_close_object + # include + json_add_array "include" + json_close_array + # plugins + json_add_array "plugins" + # plugin: geosite_cn + json_add_object + json_add_string "tag" "geosite_cn" + json_add_string "type" "domain_set" + json_add_object "args" + json_add_array "files" + json_add_string "" "/var/mosdns/geosite_cn.txt" + json_close_array + json_close_object + json_close_object + # plugin: geoip_cn + json_add_object + json_add_string "tag" "geoip_cn" + json_add_string "type" "ip_set" + json_add_object "args" + json_add_array "files" + json_add_string "" "/var/mosdns/geoip_cn.txt" + json_close_array + json_close_object + json_close_object + # plugin: geosite_apple + json_add_object + json_add_string "tag" "geosite_apple" + json_add_string "type" "domain_set" + json_add_object "args" + json_add_array "files" + json_add_string "" "/var/mosdns/geosite_apple.txt" + json_close_array + json_close_object + json_close_object + # plugin: geosite_no_cn + json_add_object + json_add_string "tag" "geosite_no_cn" + json_add_string "type" "domain_set" + json_add_object "args" + json_add_array "files" + json_add_string "" "/var/mosdns/geosite_geolocation-!cn.txt" + json_close_array + json_close_object + json_close_object + # plugin: whitelist + json_add_object + json_add_string "tag" "whitelist" + json_add_string "type" "domain_set" + json_add_object "args" + json_add_array "files" + json_add_string "" "/etc/mosdns/rule/whitelist.txt" + json_close_array + json_close_object + json_close_object + # plugin: blocklist + json_add_object + json_add_string "tag" "blocklist" + json_add_string "type" "domain_set" + json_add_object "args" + json_add_array "files" + json_add_string "" "/etc/mosdns/rule/blocklist.txt" + json_close_array + json_close_object + json_close_object + # plugin: greylist + json_add_object + json_add_string "tag" "greylist" + json_add_string "type" "domain_set" + json_add_object "args" + json_add_array "files" + json_add_string "" "/etc/mosdns/rule/greylist.txt" + json_close_array + json_close_object + json_close_object + # plugin: ddnslist + json_add_object + json_add_string "tag" "ddnslist" + json_add_string "type" "domain_set" + json_add_object "args" + json_add_array "files" + json_add_string "" "/etc/mosdns/rule/ddnslist.txt" + json_close_array + json_close_object + json_close_object + # plugin: hosts + json_add_object + json_add_string "tag" "hosts" + json_add_string "type" "hosts" + json_add_object "args" + json_add_array "files" + json_add_string "" "/etc/mosdns/rule/hosts.txt" + json_close_array + json_close_object + json_close_object + # plugin: redirect + json_add_object + json_add_string "tag" "redirect" + json_add_string "type" "redirect" + json_add_object "args" + json_add_array "files" + json_add_string "" "/etc/mosdns/rule/redirect.txt" + json_close_array + json_close_object + json_close_object + # plugin: adlist + json_add_object + json_add_string "tag" "adlist" + json_add_string "type" "domain_set" + json_add_object "args" + json_add_array "files" + adlist=$($MOSDNS_SCRIPT adlist) + for list in $adlist; do + json_add_string "" "$list" + done + json_close_array + json_close_object + json_close_object + # plugin: local_ptr + json_add_object + json_add_string "tag" "local_ptr" + json_add_string "type" "domain_set" + json_add_object "args" + json_add_array "files" + json_add_string "" "/etc/mosdns/rule/local-ptr.txt" + json_close_array + json_close_object + json_close_object + # plugin: stream_media + json_add_object + json_add_string "tag" "stream_media" + json_add_string "type" "domain_set" + json_add_object "args" + json_add_array "files" + json_add_string "" "/var/mosdns/geosite_disney.txt" + json_add_string "" "/var/mosdns/geosite_netflix.txt" + json_add_string "" "/var/mosdns/geosite_hulu.txt" + json_add_string "" "/etc/mosdns/rule/streaming.txt" + json_close_array + json_close_object + json_close_object + # plugin: cloudflare_cidr + json_add_object + json_add_string "tag" "cloudflare_cidr" + json_add_string "type" "ip_set" + json_add_object "args" + json_add_array "files" + json_add_string "" "/etc/mosdns/rule/cloudflare-cidr.txt" + json_close_array + json_close_object + json_close_object + # plugin: lazy_cache + [ "$cache" -eq 1 ] && { + json_add_object + json_add_string "tag" "lazy_cache" + json_add_string "type" "cache" + json_add_object "args" + json_add_int "size" "$cache_size" + json_add_int "lazy_cache_ttl" "$lazy_cache_ttl" + [ "$dump_file" -eq 1 ] && { + json_add_string "dump_file" "/etc/mosdns/cache.dump" + json_add_int "dump_interval" "$dump_interval" + } + json_close_object + json_close_object + } + # plugin: forward_xinfeng_udp + json_add_object + json_add_string "tag" "forward_xinfeng_udp" + json_add_string "type" "forward" + json_add_object "args" + json_add_int "concurrent" 2 + json_add_array "upstreams" + json_add_object + json_add_string "addr" "114.114.114.114" + json_close_object + json_add_object + json_add_string "addr" "114.114.115.115" + json_close_object + json_close_array + json_close_object + json_close_object + # plugin: forward_local + json_add_object + json_add_string "tag" "forward_local" + json_add_string "type" "forward" + json_add_object "args" + json_add_int "concurrent" "$concurrent" + json_add_array "upstreams" + local_dns=$($MOSDNS_SCRIPT dns) + for addr in $local_dns; do + enable_http3=0 + if echo "$addr" | grep -q "^h3://"; then + enable_http3=1 + addr=$(echo $addr | sed 's/h3:\/\//https:\/\//g') + fi + json_add_object + json_add_string "addr" "$addr" + json_add_string "bootstrap" "$bootstrap_dns" + json_add_boolean "enable_pipeline" "$enable_pipeline" + json_add_boolean "insecure_skip_verify" "$insecure_skip_verify" + json_add_int "idle_timeout" "$idle_timeout" + [ "$enable_http3" -eq 1 ] && json_add_boolean "enable_http3" "1" + json_close_object + done + json_close_array + json_close_object + json_close_object + # plugin: forward_remote + json_add_object + json_add_string "tag" "forward_remote" + json_add_string "type" "forward" + json_add_object "args" + json_add_int "concurrent" "$concurrent" + json_add_array "upstreams" + for addr in $remote_dns; do + enable_http3=0 + if echo "$addr" | grep -q "^h3://"; then + enable_http3=1 + addr=$(echo $addr | sed 's/h3:\/\//https:\/\//g') + fi + json_add_object + json_add_string "addr" "$addr" + json_add_string "bootstrap" "$bootstrap_dns" + json_add_boolean "enable_pipeline" "$enable_pipeline" + json_add_boolean "insecure_skip_verify" "$insecure_skip_verify" + json_add_int "idle_timeout" "$idle_timeout" + [ "$enable_http3" -eq 1 ] && json_add_boolean "enable_http3" "1" + json_close_object + done + json_close_array + json_close_object + json_close_object + # plugin: forward_remote_upstream + json_add_object + json_add_string "tag" "forward_remote_upstream" + json_add_string "type" "sequence" + json_add_array "args" + [ "$prefer_ipv4" -eq 1 ] && { + json_add_object + json_add_string "exec" "prefer_ipv4" + json_close_object + } + [ "$enable_ecs_remote" -eq 1 ] && { + json_add_object + json_add_string "exec" "ecs $remote_ecs_ip" + json_close_object + } + json_add_object + json_add_string "exec" "\$forward_remote" + json_close_object + json_close_array + json_close_object + # plugin: forward_stream_media + json_add_object + json_add_string "tag" "forward_stream_media" + json_add_string "type" "forward" + json_add_object "args" + json_add_int "concurrent" "$concurrent" + json_add_array "upstreams" + for addr in $stream_media_dns; do + enable_http3=0 + if echo "$addr" | grep -q "^h3://"; then + enable_http3=1 + addr=$(echo $addr | sed 's/h3:\/\//https:\/\//g') + fi + json_add_object + json_add_string "addr" "$addr" + json_add_string "bootstrap" "$bootstrap_dns" + json_add_boolean "enable_pipeline" "$enable_pipeline" + json_add_boolean "insecure_skip_verify" "$insecure_skip_verify" + json_add_int "idle_timeout" "$idle_timeout" + [ "$enable_http3" -eq 1 ] && json_add_boolean "enable_http3" "1" + json_close_object + done + json_close_array + json_close_object + json_close_object + # plugin: forward_stream_media_upstream + json_add_object + json_add_string "tag" "forward_stream_media_upstream" + json_add_string "type" "sequence" + json_add_array "args" + [ "$prefer_ipv4" -eq 1 ] && { + json_add_object + json_add_string "exec" "prefer_ipv4" + json_close_object + } + [ "$enable_ecs_remote" -eq 1 ] && { + json_add_object + json_add_string "exec" "ecs $remote_ecs_ip" + json_close_object + } + json_add_object + json_add_string "exec" "\$forward_stream_media" + json_close_object + json_close_array + json_close_object + # plugin: modify_ttl + json_add_object + json_add_string "tag" "modify_ttl" + json_add_string "type" "sequence" + json_add_array "args" + json_add_object + json_add_string "exec" "ttl $minimal_ttl-$maximum_ttl" + json_close_object + json_close_array + json_close_object + # plugin: modify_ddns_ttl + json_add_object + json_add_string "tag" "modify_ddns_ttl" + json_add_string "type" "sequence" + json_add_array "args" + json_add_object + json_add_string "exec" "ttl 5-5" + json_close_object + json_close_array + json_close_object + # plugin: has_resp_sequence + json_add_object + json_add_string "tag" "has_resp_sequence" + json_add_string "type" "sequence" + json_add_array "args" + json_add_object + json_add_string "matches" "qname \$ddnslist" + json_add_string "exec" "\$modify_ddns_ttl" + json_close_object + json_add_object + json_add_string "matches" "!qname \$ddnslist" + json_add_string "exec" "\$modify_ttl" + json_close_object + [ "$cloudflare" -eq 1 ] && { + json_add_object + json_add_array "matches" + json_add_string "" "!qname \$whitelist" + json_add_string "" "!qname \$greylist" + json_add_string "" "!qname \$stream_media" + json_add_string "" "resp_ip \$cloudflare_cidr" + json_close_array + json_add_string "exec" "black_hole $cloudflare_ip" + json_close_object + } + json_add_object + json_add_string "matches" "has_resp" + json_add_string "exec" "accept" + json_close_object + json_close_array + json_close_object + # plugin: query_is_non_local_ip + json_add_object + json_add_string "tag" "query_is_non_local_ip" + json_add_string "type" "sequence" + json_add_array "args" + json_add_object + json_add_string "exec" "\$forward_local" + json_close_object + json_add_object + json_add_string "matches" "!resp_ip \$geoip_cn" + json_add_string "exec" "drop_resp" + json_close_object + json_close_array + json_close_object + # plugin: fallback + json_add_object + json_add_string "tag" "fallback" + json_add_string "type" "fallback" + json_add_object "args" + [ "$dns_leak" -eq 1 ] && json_add_string "primary" "forward_remote_upstream" || json_add_string "primary" "query_is_non_local_ip" + json_add_string "secondary" "forward_remote_upstream" + json_add_int "threshold" 500 + json_add_boolean "always_standby" 1 + json_close_object + json_close_object + # plugin: apple_domain_fallback + json_add_object + json_add_string "tag" "apple_domain_fallback" + json_add_string "type" "fallback" + json_add_object "args" + json_add_string "primary" "query_is_non_local_ip" + json_add_string "secondary" "forward_xinfeng_udp" + json_add_int "threshold" 100 + json_add_boolean "always_standby" 1 + json_close_object + json_close_object + # plugin: query_is_apple_domain + json_add_object + json_add_string "tag" "query_is_apple_domain" + json_add_string "type" "sequence" + json_add_array "args" + json_add_object + json_add_string "matches" "!qname \$geosite_apple" + json_add_string "exec" "return" + json_close_object + json_add_object + json_add_string "exec" "\$apple_domain_fallback" + json_close_object + json_close_array + json_close_object + # plugin: query_is_ddns_domain + json_add_object + json_add_string "tag" "query_is_ddns_domain" + json_add_string "type" "sequence" + json_add_array "args" + json_add_object + json_add_string "matches" "qname \$ddnslist" + json_add_string "exec" "\$forward_local" + json_close_object + json_close_array + json_close_object + # plugin: query_is_local_domain + json_add_object + json_add_string "tag" "query_is_local_domain" + json_add_string "type" "sequence" + json_add_array "args" + [ "$prefer_ipv4_cn" -eq 1 ] && { + json_add_object + json_add_string "exec" "prefer_ipv4" + json_close_object + } + json_add_object + json_add_string "matches" "qname \$geosite_cn" + json_add_string "exec" "\$forward_local" + json_close_object + json_close_array + json_close_object + # plugin: query_is_no_local_domain + json_add_object + json_add_string "tag" "query_is_no_local_domain" + json_add_string "type" "sequence" + json_add_array "args" + json_add_object + json_add_string "matches" "qname \$geosite_no_cn" + json_add_string "exec" "\$forward_remote_upstream" + json_close_object + json_close_array + json_close_object + # plugin: query_is_whitelist_domain + json_add_object + json_add_string "tag" "query_is_whitelist_domain" + json_add_string "type" "sequence" + json_add_array "args" + [ "$prefer_ipv4_cn" -eq 1 ] && { + json_add_object + json_add_string "exec" "prefer_ipv4" + json_close_object + } + json_add_object + json_add_string "matches" "qname \$whitelist" + json_add_string "exec" "\$forward_local" + json_close_object + json_close_array + json_close_object + # plugin: query_is_greylist_domain + json_add_object + json_add_string "tag" "query_is_greylist_domain" + json_add_string "type" "sequence" + json_add_array "args" + json_add_object + json_add_string "matches" "qname \$greylist" + json_add_string "exec" "\$forward_remote_upstream" + json_close_object + json_close_array + json_close_object + # plugin: query_is_reject_domain + json_add_object + json_add_string "tag" "query_is_reject_domain" + json_add_string "type" "sequence" + json_add_array "args" + json_add_object + json_add_string "matches" "qname \$blocklist" + json_add_string "exec" "reject 3" + json_close_object + json_add_object + json_add_string "matches" "qname \$adlist" + json_add_string "exec" "reject 3" + json_close_object + json_add_object + json_add_array "matches" + json_add_string "" "qtype 12" + json_add_string "" "qname \$local_ptr" + json_close_array + json_add_string "exec" "reject 3" + json_close_object + [ "$reject_type65" -eq 1 ] && { + json_add_object + json_add_string "matches" "qtype 65" + json_add_string "exec" "reject 3" + json_close_object + } + json_close_array + json_close_object + # plugin: query_is_stream_media_domain + json_add_object + json_add_string "tag" "query_is_stream_media_domain" + json_add_string "type" "sequence" + json_add_array "args" + json_add_object + json_add_string "matches" "qname \$stream_media" + json_add_string "exec" "\$forward_stream_media_upstream" + json_close_object + json_close_array + json_close_object + # plugin: main_sequence + json_add_object + json_add_string "tag" "main_sequence" + json_add_string "type" "sequence" + json_add_array "args" + json_add_object + json_add_string "exec" "\$hosts" + json_close_object + json_add_object + json_add_string "exec" "jump has_resp_sequence" + json_close_object + [ "$cache" -eq 1 ] && { + json_add_object + json_add_array "matches" + json_add_string "" "!qname \$ddnslist" + json_add_string "" "!qname \$blocklist" + json_add_string "" "!qname \$adlist" + json_add_string "" "!qname \$local_ptr" + json_close_array + json_add_string "exec" "\$lazy_cache" + json_close_object + json_add_object + json_add_string "exec" "jump has_resp_sequence" + json_close_object + } + json_add_object + json_add_string "exec" "\$redirect" + json_close_object + json_add_object + json_add_string "exec" "jump has_resp_sequence" + json_close_object + json_add_object + [ "$apple_optimization" -eq 1 ] && { + json_add_string "exec" "\$query_is_apple_domain" + json_close_object + json_add_object + json_add_string "exec" "jump has_resp_sequence" + json_close_object + json_add_object + } + json_add_string "exec" "\$query_is_ddns_domain" + json_close_object + json_add_object + json_add_string "exec" "jump has_resp_sequence" + json_close_object + json_add_object + json_add_string "exec" "\$query_is_whitelist_domain" + json_close_object + json_add_object + json_add_string "exec" "jump has_resp_sequence" + json_close_object + json_add_object + json_add_string "exec" "\$query_is_reject_domain" + json_close_object + json_add_object + json_add_string "exec" "jump has_resp_sequence" + json_close_object + json_add_object + json_add_string "exec" "\$query_is_greylist_domain" + json_close_object + json_add_object + json_add_string "exec" "jump has_resp_sequence" + json_close_object + [ "$custom_stream_media_dns" -eq 1 ] && { + json_add_object + json_add_string "exec" "\$query_is_stream_media_domain" + json_close_object + json_add_object + json_add_string "exec" "jump has_resp_sequence" + json_close_object + } + json_add_object + json_add_string "exec" "\$query_is_local_domain" + json_close_object + json_add_object + json_add_string "exec" "jump has_resp_sequence" + json_close_object + json_add_object + json_add_string "exec" "\$query_is_no_local_domain" + json_close_object + json_add_object + json_add_string "exec" "jump has_resp_sequence" + json_close_object + json_add_object + json_add_string "exec" "\$fallback" + json_close_object + json_add_object + json_add_string "exec" "jump has_resp_sequence" + json_close_object + json_close_array + json_close_object + # plugin: udp_server + json_add_object + json_add_string "tag" "udp_server" + json_add_string "type" "udp_server" + json_add_object "args" + json_add_string "entry" "main_sequence" + json_add_string "listen" ":$listen_port" + json_close_object + json_close_object + # plugin: tcp_server + json_add_object + json_add_string "tag" "tcp_server" + json_add_string "type" "tcp_server" + json_add_object "args" + json_add_string "entry" "main_sequence" + json_add_string "listen" ":$listen_port" + json_close_object + json_close_object + # close plugins array + json_close_array + # print json + json_dump > /var/etc/mosdns.json + + # init dump_file + [ "$dump_file" -eq 1 ] && [ ! -f $DUMP_FILE ] && cp -a $DUMP_FILE_DEFAULT $DUMP_FILE + [ "$dump_file" -eq 0 ] && \cp -a $DUMP_FILE_DEFAULT $DUMP_FILE +} + +service_triggers() { + procd_add_reload_trigger "mosdns" +} + +restore_setting() { + rm -f /etc/mosdns/redirect.lock + sed -i "/list server/d" /etc/config/dhcp + uci set dhcp.@dnsmasq[0].noresolv='0' + uci del dhcp.@dnsmasq[0].cachesize + uci commit dhcp +} + +redirect_setting() { + if [ "${CONF}" = "/var/etc/mosdns.json" ]; then + sed -i "/list server/d" /etc/config/dhcp + uci add_list dhcp.@dnsmasq[0].server="127.0.0.1#$listen_port" + uci set dhcp.@dnsmasq[0].rebind_protection='0' + uci set dhcp.@dnsmasq[0].noresolv="1" + uci set dhcp.@dnsmasq[0].cachesize='0' + uci commit dhcp + else + sed -i "/list server/d" /etc/config/dhcp + uci add_list dhcp.@dnsmasq[0].server="127.0.0.1#$(awk -F'[:" ]+' '/^\s+listen:/{for(i=1;i<=NF;i++){if($i~/^[0-9]+$/){print $i;exit}}}' $CONF)" + uci set dhcp.@dnsmasq[0].rebind_protection='0' + uci set dhcp.@dnsmasq[0].noresolv="1" + uci set dhcp.@dnsmasq[0].cachesize='0' + uci commit dhcp + fi + touch /etc/mosdns/redirect.lock +} + +reload_dnsmasq() { + /etc/init.d/dnsmasq reload +} + +reload_service() { + stop + sleep 1 + start +} + +setcron() { + sed -i '/mosdns.sh/d' $CRON_FILE 2>/dev/null + [ "$geo_auto_update" -eq 1 ] && echo "0 $geo_update_day_time * * $geo_update_week_time $MOSDNS_SCRIPT geodata" >> $CRON_FILE + crontab $CRON_FILE +} + +delcron() { + sed -i '/mosdns.sh/d' $CRON_FILE 2>/dev/null + crontab $CRON_FILE +} + +v2dat_dump() { + $MOSDNS_SCRIPT v2dat_dump +} + +start_service() { + config_load "mosdns" + config_foreach get_config "mosdns" + [ $enabled -ne 1 ] && return 1 + delcron ; setcron + :> $($MOSDNS_SCRIPT logfile) + if [ "${log_level}" = "error" ] || [ "${log_level}" = "warn" ]; then + v2dat_dump > /dev/null 2>&1 + else + v2dat_dump >> $($MOSDNS_SCRIPT logfile) 2>&1 + fi + [ "${CONF}" = "/var/etc/mosdns.json" ] && generate_config + + procd_open_instance mosdns + procd_set_param env QUIC_GO_DISABLE_RECEIVE_BUFFER_WARNING=true + procd_set_param command $PROG start + procd_append_param command -c "$CONF" + procd_append_param command -d "/etc/mosdns" + procd_set_param stdout 1 + procd_set_param stderr 1 + procd_set_param respawn + procd_close_instance mosdns + [ "$redirect" -ne 1 ] && [ -f "/etc/mosdns/redirect.lock" ] && restore_setting + [ "$redirect" -eq 1 ] && redirect_setting + reload_dnsmasq + # dns hijack + if [ "$local_dns_redirect" -eq 1 ] && [ -f "/sbin/fw4" ]; then + ! nft --check list table inet mosdns > "/dev/null" 2>&1 || \ + nft delete table inet mosdns + nft add table inet mosdns + nft add chain inet mosdns prerouting "{ type nat hook prerouting priority -95; policy accept; }" + nft add rule inet mosdns prerouting "meta nfproto { ipv4, ipv6 } udp dport 53 counter redirect to :$listen_port comment \"DNS HIJACK\"" + fi + # Update Adlist + update_list=0 + if [ "$adblock" -eq 1 ]; then + if [ -f "/etc/mosdns/rule/.ad_source" ]; then + for url in $ad_source; + do + if [ "$url" = "geosite.dat" ] || [ $(echo "$url" | grep -c -E "^file://") -eq 1 ]; then + continue + fi + if [ $(grep -c "$url" "/etc/mosdns/rule/.ad_source") -eq 0 ]; then + update_list=1 + break + fi + done + else + update_list=1 + fi + fi + [ "$update_list" -eq 1 ] && $MOSDNS_SCRIPT adlist_update &> /dev/null & +} + +stop_service() { + config_load "mosdns" + config_foreach get_config "mosdns" + [ "$enabled" -eq "0" ] && [ -f "/etc/mosdns/redirect.lock" ] && restore_setting + ! nft --check list table inet mosdns > "/dev/null" 2>&1 || \ + nft delete table inet mosdns + reload_dnsmasq + delcron +} diff --git a/luci-app-mosdns/root/etc/mosdns/config_custom.yaml b/luci-app-mosdns/root/etc/mosdns/config_custom.yaml new file mode 100644 index 000000000..5e521e961 --- /dev/null +++ b/luci-app-mosdns/root/etc/mosdns/config_custom.yaml @@ -0,0 +1,143 @@ +log: + level: info + file: "/var/log/mosdns.log" + +# API 入口设置 +api: + http: "0.0.0.0:9091" + +include: [] + +plugins: + # 国内域名 + - tag: geosite_cn + type: domain_set + args: + files: + - "/var/mosdns/geosite_cn.txt" + + # 国内 IP + - tag: geoip_cn + type: ip_set + args: + files: + - "/var/mosdns/geoip_cn.txt" + + # 国外域名 + - tag: geosite_no_cn + type: domain_set + args: + files: + - "/var/mosdns/geosite_geolocation-!cn.txt" + + # 缓存 + - tag: lazy_cache + type: cache + args: + size: 20000 + lazy_cache_ttl: 86400 + dump_file: "/etc/mosdns/cache.dump" + dump_interval: 600 + + # 转发至本地服务器 + - tag: forward_local + type: forward + args: + upstreams: + - addr: "https://doh.pub/dns-query" + bootstrap: 180.76.76.76 + - addr: 119.29.29.29 + + # 转发至远程服务器 + - tag: forward_remote + type: forward + args: + upstreams: + - addr: tls://8.8.8.8 + enable_pipeline: false + + # 国内解析 + - tag: local_sequence + type: sequence + args: + - exec: $forward_local + + # 国外解析 + - tag: remote_sequence + type: sequence + args: + - exec: prefer_ipv4 + - exec: $forward_remote + + # 有响应终止返回 + - tag: has_resp_sequence + type: sequence + args: + - matches: has_resp + exec: accept + + # fallback 用本地服务器 sequence + # 返回非国内 ip 则 drop_resp + - tag: query_is_local_ip + type: sequence + args: + - exec: $local_sequence + - matches: "!resp_ip $geoip_cn" + exec: drop_resp + + # fallback 用远程服务器 sequence + - tag: query_is_remote + type: sequence + args: + - exec: $remote_sequence + + # fallback 用远程服务器 sequence + - tag: fallback + type: fallback + args: + primary: query_is_local_ip + secondary: query_is_remote + threshold: 500 + always_standby: true + + # 查询国内域名 + - tag: query_is_local_domain + type: sequence + args: + - matches: qname $geosite_cn + exec: $local_sequence + + # 查询国外域名 + - tag: query_is_no_local_domain + type: sequence + args: + - matches: qname $geosite_no_cn + exec: $remote_sequence + + # 主要的运行逻辑插件 + # sequence 插件中调用的插件 tag 必须在 sequence 前定义, + # 否则 sequence 找不到对应插件。 + - tag: main_sequence + type: sequence + args: + - exec: $lazy_cache + - exec: jump has_resp_sequence + - exec: $query_is_local_domain + - exec: jump has_resp_sequence + - exec: $query_is_no_local_domain + - exec: jump has_resp_sequence + - exec: $fallback + + # 启动 udp 服务器。 + - tag: udp_server + type: udp_server + args: + entry: main_sequence + listen: ":5335" + + # 启动 tcp 服务器。 + - tag: tcp_server + type: tcp_server + args: + entry: main_sequence + listen: ":5335" diff --git a/luci-app-mosdns/root/etc/mosdns/rule/blocklist.txt b/luci-app-mosdns/root/etc/mosdns/rule/blocklist.txt new file mode 100644 index 000000000..9a286f45e --- /dev/null +++ b/luci-app-mosdns/root/etc/mosdns/rule/blocklist.txt @@ -0,0 +1,2 @@ +# MosDNS Rules + diff --git a/luci-app-mosdns/root/etc/mosdns/rule/cloudflare-cidr.txt b/luci-app-mosdns/root/etc/mosdns/rule/cloudflare-cidr.txt new file mode 100644 index 000000000..ca50c3452 --- /dev/null +++ b/luci-app-mosdns/root/etc/mosdns/rule/cloudflare-cidr.txt @@ -0,0 +1,22 @@ +173.245.48.0/20 +103.21.244.0/22 +103.22.200.0/22 +103.31.4.0/22 +141.101.64.0/18 +108.162.192.0/18 +190.93.240.0/20 +188.114.96.0/20 +197.234.240.0/22 +198.41.128.0/17 +162.158.0.0/15 +104.16.0.0/13 +104.24.0.0/14 +172.64.0.0/13 +131.0.72.0/22 +2400:cb00::/32 +2606:4700::/32 +2803:f800::/32 +2405:b500::/32 +2405:8100::/32 +2a06:98c0::/29 +2c0f:f248::/32 diff --git a/luci-app-mosdns/root/etc/mosdns/rule/ddnslist.txt b/luci-app-mosdns/root/etc/mosdns/rule/ddnslist.txt new file mode 100644 index 000000000..9a286f45e --- /dev/null +++ b/luci-app-mosdns/root/etc/mosdns/rule/ddnslist.txt @@ -0,0 +1,2 @@ +# MosDNS Rules + diff --git a/luci-app-mosdns/root/etc/mosdns/rule/greylist.txt b/luci-app-mosdns/root/etc/mosdns/rule/greylist.txt new file mode 100644 index 000000000..9a286f45e --- /dev/null +++ b/luci-app-mosdns/root/etc/mosdns/rule/greylist.txt @@ -0,0 +1,2 @@ +# MosDNS Rules + diff --git a/luci-app-mosdns/root/etc/mosdns/rule/hosts.txt b/luci-app-mosdns/root/etc/mosdns/rule/hosts.txt new file mode 100644 index 000000000..9a286f45e --- /dev/null +++ b/luci-app-mosdns/root/etc/mosdns/rule/hosts.txt @@ -0,0 +1,2 @@ +# MosDNS Rules + diff --git a/luci-app-mosdns/root/etc/mosdns/rule/local-ptr.txt b/luci-app-mosdns/root/etc/mosdns/rule/local-ptr.txt new file mode 100644 index 000000000..0a66cae4e --- /dev/null +++ b/luci-app-mosdns/root/etc/mosdns/rule/local-ptr.txt @@ -0,0 +1,87 @@ +0.in-addr.arpa +10.in-addr.arpa +127.in-addr.arpa +16.172.in-addr.arpa +17.172.in-addr.arpa +18.172.in-addr.arpa +19.172.in-addr.arpa +20.172.in-addr.arpa +21.172.in-addr.arpa +22.172.in-addr.arpa +23.172.in-addr.arpa +24.172.in-addr.arpa +25.172.in-addr.arpa +26.172.in-addr.arpa +27.172.in-addr.arpa +28.172.in-addr.arpa +29.172.in-addr.arpa +30.172.in-addr.arpa +31.172.in-addr.arpa +64.100.in-addr.arpa +65.100.in-addr.arpa +66.100.in-addr.arpa +67.100.in-addr.arpa +68.100.in-addr.arpa +69.100.in-addr.arpa +70.100.in-addr.arpa +71.100.in-addr.arpa +72.100.in-addr.arpa +73.100.in-addr.arpa +74.100.in-addr.arpa +75.100.in-addr.arpa +76.100.in-addr.arpa +77.100.in-addr.arpa +78.100.in-addr.arpa +79.100.in-addr.arpa +80.100.in-addr.arpa +81.100.in-addr.arpa +82.100.in-addr.arpa +83.100.in-addr.arpa +84.100.in-addr.arpa +85.100.in-addr.arpa +86.100.in-addr.arpa +87.100.in-addr.arpa +88.100.in-addr.arpa +89.100.in-addr.arpa +90.100.in-addr.arpa +91.100.in-addr.arpa +92.100.in-addr.arpa +93.100.in-addr.arpa +94.100.in-addr.arpa +95.100.in-addr.arpa +96.100.in-addr.arpa +97.100.in-addr.arpa +98.100.in-addr.arpa +99.100.in-addr.arpa +100.100.in-addr.arpa +101.100.in-addr.arpa +102.100.in-addr.arpa +103.100.in-addr.arpa +104.100.in-addr.arpa +105.100.in-addr.arpa +106.100.in-addr.arpa +107.100.in-addr.arpa +108.100.in-addr.arpa +109.100.in-addr.arpa +110.100.in-addr.arpa +111.100.in-addr.arpa +112.100.in-addr.arpa +113.100.in-addr.arpa +114.100.in-addr.arpa +115.100.in-addr.arpa +116.100.in-addr.arpa +117.100.in-addr.arpa +118.100.in-addr.arpa +119.100.in-addr.arpa +120.100.in-addr.arpa +121.100.in-addr.arpa +122.100.in-addr.arpa +123.100.in-addr.arpa +124.100.in-addr.arpa +125.100.in-addr.arpa +126.100.in-addr.arpa +127.100.in-addr.arpa +2.0.192.in-addr.arpa +168.192.in-addr.arpa +255.255.255.255.in-addr.arpa +domain:ip6.arpa diff --git a/luci-app-mosdns/root/etc/mosdns/rule/redirect.txt b/luci-app-mosdns/root/etc/mosdns/rule/redirect.txt new file mode 100644 index 000000000..9a286f45e --- /dev/null +++ b/luci-app-mosdns/root/etc/mosdns/rule/redirect.txt @@ -0,0 +1,2 @@ +# MosDNS Rules + diff --git a/luci-app-mosdns/root/etc/mosdns/rule/streaming.txt b/luci-app-mosdns/root/etc/mosdns/rule/streaming.txt new file mode 100644 index 000000000..9a286f45e --- /dev/null +++ b/luci-app-mosdns/root/etc/mosdns/rule/streaming.txt @@ -0,0 +1,2 @@ +# MosDNS Rules + diff --git a/luci-app-mosdns/root/etc/mosdns/rule/whitelist.txt b/luci-app-mosdns/root/etc/mosdns/rule/whitelist.txt new file mode 100644 index 000000000..9a286f45e --- /dev/null +++ b/luci-app-mosdns/root/etc/mosdns/rule/whitelist.txt @@ -0,0 +1,2 @@ +# MosDNS Rules + diff --git a/luci-app-mosdns/root/etc/uci-defaults/luci-mosdns b/luci-app-mosdns/root/etc/uci-defaults/luci-mosdns new file mode 100755 index 000000000..86e26eab4 --- /dev/null +++ b/luci-app-mosdns/root/etc/uci-defaults/luci-mosdns @@ -0,0 +1,13 @@ +#!/bin/sh + +[ -f "/etc/config/ucitrack" ] && { +uci -q batch <<-EOF >/dev/null + delete ucitrack.@mosdns[-1] + add ucitrack mosdns + set ucitrack.@mosdns[-1].init=mosdns + commit ucitrack +EOF +} + +rm -rf /tmp/luci-* +exit 0 diff --git a/luci-app-mosdns/root/usr/share/luci/menu.d/luci-app-mosdns.json b/luci-app-mosdns/root/usr/share/luci/menu.d/luci-app-mosdns.json new file mode 100644 index 000000000..e866a8f17 --- /dev/null +++ b/luci-app-mosdns/root/usr/share/luci/menu.d/luci-app-mosdns.json @@ -0,0 +1,45 @@ +{ + "admin/services/mosdns": { + "title": "MosDNS", + "order": 30, + "action": { + "type": "firstchild" + }, + "depends": { + "acl": [ "luci-app-mosdns" ], + "uci": { "mosdns": true } + } + }, + "admin/services/mosdns/basic": { + "title": "Basic Setting", + "order": 10, + "action": { + "type": "view", + "path": "mosdns/basic" + } + }, + "admin/services/mosdns/rules": { + "title": "Rules", + "order": 15, + "action": { + "type": "view", + "path": "mosdns/rules" + } + }, + "admin/services/mosdns/update": { + "title": "Geodata Update", + "order": 20, + "action": { + "type": "view", + "path": "mosdns/update" + } + }, + "admin/services/mosdns/logs": { + "title": "Logs", + "order": 25, + "action": { + "type": "view", + "path": "mosdns/logs" + } + } +} diff --git a/luci-app-mosdns/root/usr/share/mosdns/cache.dump b/luci-app-mosdns/root/usr/share/mosdns/cache.dump new file mode 100644 index 000000000..7d5741882 Binary files /dev/null and b/luci-app-mosdns/root/usr/share/mosdns/cache.dump differ diff --git a/luci-app-mosdns/root/usr/share/mosdns/mosdns.sh b/luci-app-mosdns/root/usr/share/mosdns/mosdns.sh new file mode 100755 index 000000000..b0534daef --- /dev/null +++ b/luci-app-mosdns/root/usr/share/mosdns/mosdns.sh @@ -0,0 +1,210 @@ +#!/bin/sh + +script_action=${1} + +logfile_path() { + configfile=$(uci -q get mosdns.config.configfile) + if [ "$configfile" = "/var/etc/mosdns.json" ]; then + uci -q get mosdns.config.log_file + else + [ ! -f /etc/mosdns/config_custom.yaml ] && exit 1 + awk '/^log:/{f=1;next}f==1{if($0~/file:/){print;exit}if($0~/^[^ ]/)exit}' /etc/mosdns/config_custom.yaml | grep -Eo "/[^'\"]+" + fi +} + +print_logfile() { + cat $(logfile_path); +} + +clean_logfile() { + true > $(logfile_path); +} + +interface_dns() ( + if [ "$(uci -q get mosdns.config.custom_local_dns)" = 1 ]; then + uci -q get mosdns.config.local_dns + else + peerdns=$(uci -q get network.wan.peerdns) + proto=$(uci -q get network.wan.proto) + if [ "$peerdns" = 0 ] || [ "$proto" = "static" ]; then + uci -q get network.wan.dns + else + interface_status=$(ubus call network.interface.wan status) + echo $interface_status | jsonfilter -e "@['dns-server'][0]" + echo $interface_status | jsonfilter -e "@['dns-server'][1]" + fi + [ $? -ne 0 ] && echo "119.29.29.29 223.5.5.5" + fi +) + +get_adlist() ( + adblock=$(uci -q get mosdns.config.adblock) + if [ "$adblock" = 1 ]; then + mkdir -p /etc/mosdns/rule/adlist + ad_source=$(uci -q get mosdns.config.ad_source) + for url in $ad_source; + do + if [ $(echo $url) = 'geosite.dat' ]; then + echo "/var/mosdns/geosite_category-ads-all.txt" + elif echo "$url" | grep -Eq "^file://" ; then + echo "$url" | sed 's/file:\/\///' + else + echo "/etc/mosdns/rule/adlist/$(basename $url)" + [ ! -f "/etc/mosdns/rule/adlist/$(basename $url)" ] && touch /etc/mosdns/rule/adlist/$(basename $url) + fi + done + else + rm -rf /etc/mosdns/rule/adlist /etc/mosdns/rule/.ad_source + touch /var/mosdns/disable-ads.txt + echo "/var/mosdns/disable-ads.txt" + fi +) + +adlist_update() { + [ "$(uci -q get mosdns.config.adblock)" != 1 ] && return 0 + lock_file=/var/lock/mosdns_ad_update.lock + ad_source=$(uci -q get mosdns.config.ad_source) + : > /etc/mosdns/rule/.ad_source + if [ -f "$lock_file" ]; then + has_update=0 + exit 0 + else + : > $lock_file + fi + AD_TMPDIR=$(mktemp -d) || exit 1 + has_update=0 + for url in $ad_source; + do + if [ "$url" != "geosite.dat" ] && [ $(echo "$url" | grep -c -E "^file://") -eq 0 ]; then + has_update=1 + echo "$url" >> /etc/mosdns/rule/.ad_source + filename=$(basename $url) + if echo "$url" | grep -Eq "^https://raw.githubusercontent.com" ; then + [ -n "$(uci -q get mosdns.config.github_proxy)" ] && mirror="$(uci -q get mosdns.config.github_proxy)/" + else + mirror="" + fi + echo -e "Downloading $mirror$url" + curl --connect-timeout 5 -m 90 --ipv4 -kfSLo "$AD_TMPDIR/$filename" "$mirror$url" + fi + done + if [ $? -ne 0 ]; then + echo -e "\e[1;31mRules download failed." + rm -rf "$AD_TMPDIR" "$lock_file" + exit 1 + else + [ $has_update -eq 1 ] && { + mkdir -p /etc/mosdns/rule/adlist + rm -rf /etc/mosdns/rule/adlist/* + \cp $AD_TMPDIR/* /etc/mosdns/rule/adlist + } + fi + rm -rf "$AD_TMPDIR" "$lock_file" +} + +geodat_update() ( + TMPDIR=$(mktemp -d) || exit 1 + [ -n "$(uci -q get mosdns.config.github_proxy)" ] && mirror="$(uci -q get mosdns.config.github_proxy)/" + # geoip.dat - cn-private + geoip_type=$(uci -q get mosdns.config.geoip_type || echo "geoip-only-cn-private") + echo -e "Downloading "$mirror"https://github.com/Loyalsoldier/geoip/releases/latest/download/"$geoip_type".dat" + curl --connect-timeout 5 -m 120 --ipv4 -kfSLo "$TMPDIR/geoip.dat" ""$mirror"https://github.com/Loyalsoldier/geoip/releases/latest/download/"$geoip_type".dat" + [ $? -ne 0 ] && rm -rf "$TMPDIR" && exit 1 + # checksum - geoip.dat + echo -e "Downloading "$mirror"https://github.com/Loyalsoldier/geoip/releases/latest/download/"$geoip_type".dat.sha256sum" + curl --connect-timeout 5 -m 20 --ipv4 -kfSLo "$TMPDIR/geoip.dat.sha256sum" ""$mirror"https://github.com/Loyalsoldier/geoip/releases/latest/download/"$geoip_type".dat.sha256sum" + [ $? -ne 0 ] && rm -rf "$TMPDIR" && exit 1 + if [ "$(sha256sum "$TMPDIR/geoip.dat" | awk '{print $1}')" != "$(cat "$TMPDIR/geoip.dat.sha256sum" | awk '{print $1}')" ]; then + echo -e "\e[1;31mgeoip.dat checksum error" + rm -rf "$TMPDIR" + exit 1 + fi + + # geosite.dat + echo -e "Downloading "$mirror"https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat" + curl --connect-timeout 5 -m 120 --ipv4 -kfSLo "$TMPDIR/geosite.dat" ""$mirror"https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat" + [ $? -ne 0 ] && rm -rf "$TMPDIR" && exit 1 + # checksum - geosite.dat + echo -e "Downloading "$mirror"https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat.sha256sum" + curl --connect-timeout 5 -m 20 --ipv4 -kfSLo "$TMPDIR/geosite.dat.sha256sum" ""$mirror"https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat.sha256sum" + [ $? -ne 0 ] && rm -rf "$TMPDIR" && exit 1 + if [ "$(sha256sum "$TMPDIR/geosite.dat" | awk '{print $1}')" != "$(cat "$TMPDIR/geosite.dat.sha256sum" | awk '{print $1}')" ]; then + echo -e "\e[1;31mgeosite.dat checksum error" + rm -rf "$TMPDIR" + exit 1 + fi + rm -rf "$TMPDIR"/*.sha256sum + \cp -a "$TMPDIR"/* /usr/share/v2ray + rm -rf "$TMPDIR" +) + +restart_service() { + /etc/init.d/mosdns restart +} + +flush_cache() { + curl -s 127.0.0.1:$(uci -q get mosdns.config.listen_port_api)/plugins/lazy_cache/flush || exit 1 +} + +v2dat_dump() { + # env + v2dat_dir=/usr/share/v2ray + adblock=$(uci -q get mosdns.config.adblock) + ad_source=$(uci -q get mosdns.config.ad_source) + configfile=$(uci -q get mosdns.config.configfile) + streaming_media=$(uci -q get mosdns.config.custom_stream_media_dns) + mkdir -p /var/mosdns + rm -f /var/mosdns/geo*.txt + if [ "$configfile" = "/var/etc/mosdns.json" ]; then + # default config + v2dat unpack geoip -o /var/mosdns -f cn $v2dat_dir/geoip.dat + v2dat unpack geosite -o /var/mosdns -f cn -f apple -f 'geolocation-!cn' $v2dat_dir/geosite.dat + [ "$adblock" = 1 ] && [ $(echo $ad_source | grep -c geosite.dat) -ge '1' ] && v2dat unpack geosite -o /var/mosdns -f category-ads-all $v2dat_dir/geosite.dat + [ "$streaming_media" = 1 ] && v2dat unpack geosite -o /var/mosdns -f netflix -f disney -f hulu $v2dat_dir/geosite.dat || \ + touch /var/mosdns/geosite_disney.txt ; touch /var/mosdns/geosite_netflix.txt ; touch /var/mosdns/geosite_hulu.txt + else + # custom config + v2dat unpack geoip -o /var/mosdns -f cn $v2dat_dir/geoip.dat + v2dat unpack geosite -o /var/mosdns -f cn -f 'geolocation-!cn' $v2dat_dir/geosite.dat + geoip_tags=$(uci -q get mosdns.config.geoip_tags) + geosite_tags=$(uci -q get mosdns.config.geosite_tags) + [ -n "$geoip_tags" ] && v2dat unpack geoip -o /var/mosdns $(echo $geoip_tags | sed -r 's/\S+/-f &/g') $v2dat_dir/geoip.dat + [ -n "$geosite_tags" ] && v2dat unpack geosite -o /var/mosdns $(echo $geosite_tags | sed -r 's/\S+/-f &/g') $v2dat_dir/geosite.dat + fi +} + +case $script_action in + "dns") + interface_dns + ;; + "adlist") + get_adlist + ;; + "geodata") + geodat_update && adlist_update && restart_service + ;; + "logfile") + logfile_path + ;; + "adlist_update") + adlist_update && [ "$has_update" -eq 1 ] && restart_service + ;; + "flush") + flush_cache + ;; + "v2dat_dump") + v2dat_dump + ;; + "printlog") + print_logfile + ;; + "cleanlog") + clean_logfile + ;; + "version") + mosdns version + ;; + *) + exit 0 + ;; +esac diff --git a/luci-app-mosdns/root/usr/share/rpcd/acl.d/luci-app-mosdns.json b/luci-app-mosdns/root/usr/share/rpcd/acl.d/luci-app-mosdns.json new file mode 100644 index 000000000..3dc05af2a --- /dev/null +++ b/luci-app-mosdns/root/usr/share/rpcd/acl.d/luci-app-mosdns.json @@ -0,0 +1,46 @@ +{ + "luci-app-mosdns": { + "description": "Grant UCI access for luci-app-mosdns", + "read": { + "file": { + "/etc/init.d/mosdns": [ "exec" ], + "/etc/mosdns/config_custom.yaml": [ "read" ], + "/etc/mosdns/rule/blocklist.txt": [ "read" ], + "/etc/mosdns/rule/cloudflare-cidr.txt": [ "read" ], + "/etc/mosdns/rule/ddnslist.txt": [ "read" ], + "/etc/mosdns/rule/greylist.txt": [ "read" ], + "/etc/mosdns/rule/hosts.txt": [ "read" ], + "/etc/mosdns/rule/local-ptr.txt": [ "read" ], + "/etc/mosdns/rule/redirect.txt": [ "read" ], + "/etc/mosdns/rule/streaming.txt": [ "read" ], + "/etc/mosdns/rule/whitelist.txt": [ "read" ], + "/usr/bin/mosdns": [ "exec" ], + "/usr/share/mosdns/mosdns.sh": [ "exec" ] + }, + "ubus": { + "file": [ "read" ], + "service": [ "list" ] + }, + "uci": [ "mosdns" ] + }, + "write": { + "file": { + "/etc/mosdns/config_custom.yaml": [ "write" ], + "/etc/mosdns/rule/blocklist.txt": [ "write" ], + "/etc/mosdns/rule/cloudflare-cidr.txt": [ "write" ], + "/etc/mosdns/rule/ddnslist.txt": [ "write" ], + "/etc/mosdns/rule/greylist.txt": [ "write" ], + "/etc/mosdns/rule/hosts.txt": [ "write" ], + "/etc/mosdns/rule/local-ptr.txt": [ "write" ], + "/etc/mosdns/rule/redirect.txt": [ "write" ], + "/etc/mosdns/rule/streaming.txt": [ "write" ], + "/etc/mosdns/rule/whitelist.txt": [ "write" ] + }, + "ubus": { + "file": [ "write" ] + }, + "uci": [ "mosdns" ] + } + } +} + diff --git a/luci-app-mosdns/root/usr/share/ucitrack/luci-app-mosdns.json b/luci-app-mosdns/root/usr/share/ucitrack/luci-app-mosdns.json new file mode 100644 index 000000000..c6b8867f2 --- /dev/null +++ b/luci-app-mosdns/root/usr/share/ucitrack/luci-app-mosdns.json @@ -0,0 +1,4 @@ +{ + "config": "mosdns", + "init": "mosdns" +} diff --git a/luci-app-natter/Makefile b/luci-app-natter/Makefile new file mode 100755 index 000000000..71d10d099 --- /dev/null +++ b/luci-app-natter/Makefile @@ -0,0 +1,15 @@ +# Copyright (C) 2020-2024 Hyy2001X + +include $(TOPDIR)/rules.mk + +PKG_NAME:=luci-app-natter +PKG_VERSION:=1.1 +PKG_RELEASE:=5 + +LUCI_TITLE:=LuCI Support for Natter +LUCI_PKGARCH:=all +LUCI_DEPENDS:=+natter + +include $(TOPDIR)/feeds/luci/luci.mk + +# call BuildPackage - OpenWrt buildroot signature diff --git a/luci-app-natter/README.md b/luci-app-natter/README.md new file mode 100644 index 000000000..dded3783c --- /dev/null +++ b/luci-app-natter/README.md @@ -0,0 +1,3 @@ +# luci-app-natter + +项目地址: https://github.com/MikeWang000000/Natter diff --git a/luci-app-natter/luasrc/controller/natter.lua b/luci-app-natter/luasrc/controller/natter.lua new file mode 100755 index 000000000..787a32b5e --- /dev/null +++ b/luci-app-natter/luasrc/controller/natter.lua @@ -0,0 +1,21 @@ +module("luci.controller.natter",package.seeall) + +function index() + if not nixio.fs.access("/etc/config/natter") then + return + end + entry({"admin", "network", "natter"}, alias("admin", "network", "natter", "base"), _("Natter"), 99).dependent = true + entry({"admin", "network", "natter", "base"}, cbi("natter/base"), _("Base Settings"), 10).leaf = true + entry({"admin", "network", "natter", "ports"}, cbi("natter/ports")).leaf = true + entry({"admin", "network", "natter", "log"}, form("natter/log"), _("Log"), 20).leaf = true + entry({"admin", "network", "natter", "print_log"}, call("print_log")).leaf = true + entry({"admin", "network", "natter", "del_log"}, call("del_log")).leaf = true +end + +function print_log() + luci.http.write(luci.sys.exec("sh /usr/share/luci-app-natter/log.sh print")) +end + +function del_log() + luci.http.write(luci.sys.exec("sh /usr/share/luci-app-natter/log.sh del")) +end diff --git a/luci-app-natter/luasrc/model/cbi/natter/base.lua b/luci-app-natter/luasrc/model/cbi/natter/base.lua new file mode 100755 index 000000000..4b1778568 --- /dev/null +++ b/luci-app-natter/luasrc/model/cbi/natter/base.lua @@ -0,0 +1,81 @@ +m = Map("natter", translate("Natter"), translate("Open Port under FullCone NAT (NAT 1)")) +s = m:section(TypedSection, "base") + +s.addremove = false +s.anonymous = true + +local function check_file(e) + return luci.sys.exec('ls "%s" 2> /dev/null' % e) ~= "" and true or false +end + +enable = s:option(Flag, "enable", translate("Enable")) +enable.default = 0 + +if check_file("/tmp/natter_nat_type") then + natter_nat_type_tcp = luci.sys.exec ("grep TCP /tmp/natter_nat_type") + natter_nat_type_udp = luci.sys.exec ("grep UDP /tmp/natter_nat_type") + nat_check = s:option (Button, "nat_check", translate("Check NAT Status"), translate("") .. "

" .. natter_nat_type_tcp .. "

" .. natter_nat_type_udp) +else + nat_check = s:option (Button, "nat_check", translate("Check NAT Status")) +end + +nat_check.inputtitle = translate("Exec") +nat_check.write = function() + luci.sys.call ("sh /usr/share/luci-app-natter/natcheck.sh > /tmp/natter_nat_type") + luci.http.redirect(luci.dispatcher.build_url("admin", "network", "natter", "base")) +end + +local_ip = s:option(Value, "local_ip", translate("Local IP Address"), translate("Natter Listening Address")) +local_ip.default = "0.0.0.0" +local_ip.placeholder = "0.0.0.0" +local_ip.datatype = "host" +local_ip.rmempty = false + +log_path = s:option(Value, "log_path", translate("Log Path"), translate("Directory to save natter logs")) +log_path.default = "/tmp/natter" +log_path.placeholder = "/tmp/natter" +log_path.rmempty = false + +keep_alive_server = s:option(Value, "keep_alive_server", translate("Keep Alive Server"), translate("Please ensure that the address can be connected by Natter")) +keep_alive_server.rmempty = false + +tcp_stun_server = s:option(DynamicList, "tcp_stun_server", translate("TCP STUN Server"), translate("Please DO NOT handle the IP address/domain name/port of the TCP/UDP STUN server (3478) while running proxy")) +udp_stun_server = s:option(DynamicList, "udp_stun_server", translate("UDP STUN Server")) +udp_stun_server.rmempty = false + +s = m:section(TypedSection, "ports", translate("Port Settings")) +s.anonymous = true +s.addremove = true +s.template = "cbi/tblsection" +s.extedit = luci.dispatcher.build_url("admin", "network", "natter", "ports", "%s") +function s.create(...) + local sid = TypedSection.create(...) + if sid then + luci.http.redirect(s.extedit % sid) + return + end +end + +enable_port = s:option(Flag, "enable_port", translate("Enable")) +enable_port.default = 1 +enable_port.width = "5%" + +remarks = s:option(DummyValue, "remarks", translate("Remarks")) +remarks.width = "8%" + +external_port_tcp = s:option(DummyValue, "external_port_tcp", translate("External TCP Port")) +external_port_tcp.width = "12%" + +external_port_udp = s:option(DummyValue, "external_port_udp", translate("External UDP Port")) +external_port_udp.width = "12%" + +enable_forward = s:option(Flag, "enable_forward", translate("Forward")) +enable_forward.default = 0 + +internal_ip = s:option(DummyValue, "internal_ip", translate("Internal IP Address")) +internal_ip.width = "12%" + +internal_port = s:option(DummyValue, "internal_port", translate("Internal Port")) +internal_port.width = "12%" + +return m diff --git a/luci-app-natter/luasrc/model/cbi/natter/log.lua b/luci-app-natter/luasrc/model/cbi/natter/log.lua new file mode 100755 index 000000000..fec1ba0ec --- /dev/null +++ b/luci-app-natter/luasrc/model/cbi/natter/log.lua @@ -0,0 +1,6 @@ +log = SimpleForm("natter") +log.reset = false +log.submit = false +log:append(Template("natter/natter_log")) + +return log diff --git a/luci-app-natter/luasrc/model/cbi/natter/ports.lua b/luci-app-natter/luasrc/model/cbi/natter/ports.lua new file mode 100755 index 000000000..ee6aee607 --- /dev/null +++ b/luci-app-natter/luasrc/model/cbi/natter/ports.lua @@ -0,0 +1,65 @@ +m = Map("natter", translate("Port Settings")) +m.redirect = luci.dispatcher.build_url("admin", "network", "natter") + +s = m:section(NamedSection, arg[1], "ports", "") +s.addremove = false +s.dynamic = false + +enable_port = s:option(Flag, "enable_port", translate("Enable")) + +local rand_id = luci.sys.exec("cut -d '-' -f1 /proc/sys/kernel/random/uuid 2> /dev/null") +id = s:option(Value, "id", translate("ID"), translate("Just keep default, or ensure uniqueness")) +id.default = rand_id + +remarks = s:option(Value, "remarks", translate("Remarks")) +remarks.rmempty = false + +forward_mode = s:option(ListValue, "forward_mode", translate("Forward Mode")) +forward_mode:value('1', translate("1 - Natter")) +forward_mode:value('2', translate("2 - Firewall")) +forward_mode.default = 1 + +external_port = s:option(Value, "external_port", translate("External Port"), translate("Specify the port opened by Natter")) +external_port.datatype = "port" +external_port:depends({forward_mode = "2"}) + +port_type = s:option(ListValue, "port_type", translate("Port Type")) +port_type:value("udp", translate("UDP")) +port_type:value("tcp", translate("TCP")) +port_type:value("both", translate("TCP + UDP")) +port_type.default = both +port_type.rempty = false + +enable_forward = s:option(Flag, "enable_forward", translate("Enable Port Forward"), translate("Forward opened port to internal host")) +enable_forward.default = 1 +enable_forward.rempty = false + +internal_ip = s:option(Value, "internal_ip", translate("Internal IP address"), translate("Internal Host IP address")) +internal_ip.datatype = "ipmask4" +internal_ip:depends({enable_forward = "1"}) +luci.sys.net.ipv4_hints( +function(ip, name) + internal_ip:value(ip, "%s (%s)" %{ ip, name }) +end) + +internal_port = s:option(Value, "internal_port", translate("Internal Port"), translate("Internal Host Port")) +internal_port.datatype = "port" +internal_port:depends({enable_forward = "1"}) + +delay = s:option(Value, "delay", translate("Start delay (Seconds)")) +delay.default = 0 +delay.datatype = "uinteger" +delay.rmempty = false + +log_level = s:option(ListValue, "log_level", translate("Log Level")) +log_level:value('debug', translate("Debug")) +log_level:value('info', translate("Info")) +log_level:value('warning', translate("Warning")) +log_level:value('error', translate("Error")) + +--[[ +hook = s:option(Value, "hook", translate("Hook")) +hook.rmempty = true +--]] + +return m diff --git a/luci-app-natter/luasrc/view/natter/natter_log.htm b/luci-app-natter/luasrc/view/natter/natter_log.htm new file mode 100755 index 000000000..deeff8b6b --- /dev/null +++ b/luci-app-natter/luasrc/view/natter/natter_log.htm @@ -0,0 +1,29 @@ + + +
+ + +
diff --git a/luci-app-natter/po/zh-cn/natter.po b/luci-app-natter/po/zh-cn/natter.po new file mode 100755 index 000000000..1c487042e --- /dev/null +++ b/luci-app-natter/po/zh-cn/natter.po @@ -0,0 +1,120 @@ +msgid "Natter" +msgstr "Natter" + +msgid "Open Port under FullCone NAT (NAT 1)" +msgstr "帮助 Full cone NAT (NAT 1) 用户打开公网端口" + +msgid "Log Path" +msgstr "日志路径" + +msgid "Log Level" +msgstr "日志等级" + +msgid "Base Settings" +msgstr "基础设置" + +msgid "Log" +msgstr "日志" + +msgid "IP Address" +msgstr "IP 地址" + +msgid "Start delay (Seconds)" +msgstr "启动延迟 (秒)" + +msgid "Start delay" +msgstr "启动延迟" + +msgid "Port Settings" +msgstr "端口设置" + +msgid "TCP STUN Server" +msgstr "TCP STUN 服务器" + +msgid "UDP STUN Server" +msgstr "UDP STUN 服务器" + +msgid "Keep Alive Server" +msgstr "Keep Alive 服务器" + +msgid "Directory to save natter logs" +msgstr "Natter 运行日志保存路径" + +msgid "ID" +msgstr "标识" + +msgid "Just keep default, or ensure uniqueness" +msgstr "保持默认即可, 修改时请确保标识的唯一性" + +msgid "Remarks" +msgstr "备注" + +msgid "Internal Port" +msgstr "内部端口" + +msgid "Internal Host IP address" +msgstr "内部主机 IP 地址" + +msgid "Internal Host Port" +msgstr "内部主机端口" + +msgid "External Port" +msgstr "外部端口" + +msgid "External TCP Port" +msgstr "外部 TCP 端口" + +msgid "External UDP Port" +msgstr "外部 UDP 端口" + +msgid "Specify the port opened by Natter" +msgstr "指定 Natter 打开的端口" + +msgid "Enable Port Forward" +msgstr "启用端口转发" + +msgid "Forward opened port to internal host" +msgstr "将打开的端口转发至内部主机" + +msgid "Port Type" +msgstr "端口类型" + +msgid "FullCone NAT" +msgstr "完全圆锥型 NAT" + +msgid "Please DO NOT handle the IP address/domain name/port of the TCP/UDP STUN server (3478) while running proxy" +msgstr "请不要使用任何代理软件代理 STUN 服务器地址" + +msgid "Local IP Address" +msgstr "本地 IP 地址" + +msgid "Natter Listening Address" +msgstr "Natter 监听地址" + +msgid "Please ensure that the address can be connected by Natter" +msgstr "请确保 Keep Alive 服务器地址能被 Natter 连接" + +msgid "Internal IP Address" +msgstr "内部 IP 地址" + +msgid "Forward Mode" +msgstr "转发策略" + +msgid "1 - Natter" +msgstr "Natter 内置" + +msgid "2 - Firewall" +msgstr "防火墙" + +msgid "Delete Logs" +msgstr "清除日志" + +msgid "<%:Delete Logs%>" +msgstr "清除日志" + +msgid "Check NAT Status" +msgstr "NAT 类型检测" + +msgid "Exec" +msgstr "执行" + diff --git a/luci-app-natter/po/zh_Hans/natter.po b/luci-app-natter/po/zh_Hans/natter.po new file mode 100644 index 000000000..1c487042e --- /dev/null +++ b/luci-app-natter/po/zh_Hans/natter.po @@ -0,0 +1,120 @@ +msgid "Natter" +msgstr "Natter" + +msgid "Open Port under FullCone NAT (NAT 1)" +msgstr "帮助 Full cone NAT (NAT 1) 用户打开公网端口" + +msgid "Log Path" +msgstr "日志路径" + +msgid "Log Level" +msgstr "日志等级" + +msgid "Base Settings" +msgstr "基础设置" + +msgid "Log" +msgstr "日志" + +msgid "IP Address" +msgstr "IP 地址" + +msgid "Start delay (Seconds)" +msgstr "启动延迟 (秒)" + +msgid "Start delay" +msgstr "启动延迟" + +msgid "Port Settings" +msgstr "端口设置" + +msgid "TCP STUN Server" +msgstr "TCP STUN 服务器" + +msgid "UDP STUN Server" +msgstr "UDP STUN 服务器" + +msgid "Keep Alive Server" +msgstr "Keep Alive 服务器" + +msgid "Directory to save natter logs" +msgstr "Natter 运行日志保存路径" + +msgid "ID" +msgstr "标识" + +msgid "Just keep default, or ensure uniqueness" +msgstr "保持默认即可, 修改时请确保标识的唯一性" + +msgid "Remarks" +msgstr "备注" + +msgid "Internal Port" +msgstr "内部端口" + +msgid "Internal Host IP address" +msgstr "内部主机 IP 地址" + +msgid "Internal Host Port" +msgstr "内部主机端口" + +msgid "External Port" +msgstr "外部端口" + +msgid "External TCP Port" +msgstr "外部 TCP 端口" + +msgid "External UDP Port" +msgstr "外部 UDP 端口" + +msgid "Specify the port opened by Natter" +msgstr "指定 Natter 打开的端口" + +msgid "Enable Port Forward" +msgstr "启用端口转发" + +msgid "Forward opened port to internal host" +msgstr "将打开的端口转发至内部主机" + +msgid "Port Type" +msgstr "端口类型" + +msgid "FullCone NAT" +msgstr "完全圆锥型 NAT" + +msgid "Please DO NOT handle the IP address/domain name/port of the TCP/UDP STUN server (3478) while running proxy" +msgstr "请不要使用任何代理软件代理 STUN 服务器地址" + +msgid "Local IP Address" +msgstr "本地 IP 地址" + +msgid "Natter Listening Address" +msgstr "Natter 监听地址" + +msgid "Please ensure that the address can be connected by Natter" +msgstr "请确保 Keep Alive 服务器地址能被 Natter 连接" + +msgid "Internal IP Address" +msgstr "内部 IP 地址" + +msgid "Forward Mode" +msgstr "转发策略" + +msgid "1 - Natter" +msgstr "Natter 内置" + +msgid "2 - Firewall" +msgstr "防火墙" + +msgid "Delete Logs" +msgstr "清除日志" + +msgid "<%:Delete Logs%>" +msgstr "清除日志" + +msgid "Check NAT Status" +msgstr "NAT 类型检测" + +msgid "Exec" +msgstr "执行" + diff --git a/luci-app-natter/root/etc/config/natter b/luci-app-natter/root/etc/config/natter new file mode 100755 index 000000000..a7fd285b7 --- /dev/null +++ b/luci-app-natter/root/etc/config/natter @@ -0,0 +1,15 @@ +config base + option enable '0' + option keep_alive_server "www.baidu.com" + list tcp_stun_server 'stun.nextcloud.com' + list tcp_stun_server 'fwa.lifesizecloud.com' + list tcp_stun_server 'stun.isp.net.au' + list tcp_stun_server 'stun.freeswitch.org' + list tcp_stun_server 'stun.voip.blackberry.com' + list tcp_stun_server 'stun.stunprotocol.org' + list tcp_stun_server 'stun.sipnet.com' + list tcp_stun_server 'stun.radiojar.com' + list tcp_stun_server 'stun.sonetel.com' + list tcp_stun_server 'stun.voipgate.com' + list udp_stun_server 'stun.miwifi.com' + list udp_stun_server 'stun.qq.com' diff --git a/luci-app-natter/root/etc/init.d/natter b/luci-app-natter/root/etc/init.d/natter new file mode 100755 index 000000000..3ebbad4fb --- /dev/null +++ b/luci-app-natter/root/etc/init.d/natter @@ -0,0 +1,229 @@ +#!/bin/sh /etc/rc.common + +START=98 + +start_service() { + local basic_list="enable log_path tcp_stun_server udp_stun_server keep_alive_url local_ip" + local port_list="enable_port id remarks port enable_forward \ + forward_mode external_port port_type delay log_level \ + internal_ip internal_port hook" + for i in $basic_list + do + local eval $i="$(uci_get_by_type base 0 $i)" + done ; unset i + if [ "$enable" == 1 ] + then + include_file=/var/etc/natter.include + echo " +#!/bin/sh +iptables -N natter 2> /dev/null +iptables -I INPUT -j natter 2> /dev/null +" \ + > $include_file + mkdir -p ${log_path} + iptables_remove_rule + mkdir -p /var/etc/natter + for u in $(seq 0 $(($(uci show natter 2> /dev/null | egrep '@ports\[[0-9]\]+=ports' | wc -l) - 1))) + do + for i in $port_list + do + local eval $i="$(uci_get_by_type ports $u $i)" + echo "$i : $(uci_get_by_type ports $u $i)" + done ; unset i + + [ "$enable_port" != 1 ] && continue + + case $port_type in + tcp | udp) + eval external_${port_type}="$local_ip:$external_port" + eval internal_${port_type}="$internal_ip:$internal_port" + iptables_type=$port_type + ;; + both) + external_tcp="$local_ip:$external_port" + external_udp="$local_ip:$external_port" + internal_tcp="$internal_ip:$internal_port" + internal_udp="$internal_ip:$internal_port" + iptables_type="tcp udp" + ;; + esac + + log_file=${log_path}/natter-${id}-${remarks}.log + json_file=/var/etc/natter/natter-${id}-${remarks}.json + status_file=${log_path}/natter-${id}-${remarks}.json + + echo "{ + \"logging\": { + \"level\": \"$log_level\", + \"log_file\": \"${log_file}\" + }, + \"status_report\": { + \"hook\": \"$hook\", + \"status_file\": \"${status_file}\" + }, + $( + case ${forward_mode} in + 1) + echo " + \"open_port\": { + \"tcp\": [ + + ], + \"udp\": [ + + ] + }," + echo " + \"forward_port\": { + \"tcp\": [ + $([ "$internal_tcp" ] && echo \"${internal_tcp}\") + ], + \"udp\": [ + $([ "$internal_udp" ] && echo \"${internal_udp}\") + ] + }," + ;; + 2) + echo " + \"open_port\": { + \"tcp\": [ + $([ "$external_tcp" ] && echo \"${external_tcp}\") + ], + \"udp\": [ + $([ "$external_udp" ] && echo \"${external_udp}\") + ] + }," + echo " + \"forward_port\": { + \"tcp\": [ + + ], + \"udp\": [ + + ] + }," + ;; + esac + ) + \"stun_server\": { + \"tcp\": $( + printf "[" + j=1 ; for i in $tcp_stun_server + do + [[ "$j" == 1 ]] && unset j || printf ", " + printf '"%s"' $i + done ; unset i j + printf "],") + \"udp\": $( + printf "[" + j=1 ; for i in $udp_stun_server + do + [[ "$j" == 1 ]] && unset j || printf ", " + printf '"%s"' $i + done ; unset i j + printf "]") + }, + \"keep_alive\": \"$keep_alive_url\" +}" \ + > $json_file + echo "json File: $json_file" + echo "log File: $log_file" + echo "status File: $status_file" + sleep $delay + for i in $(ps -efww | egrep 'natter.py' | grep -v grep | grep -v $$ | grep "$id" | awk '{print $1}') + do + kill -9 "$i" 2> /dev/null + done + $(command -v python) /usr/share/natter/natter.py -c $json_file & + if [ "$enable_forward" == 1 ] + then + case $forward_mode in + 1) + : + ;; + 2) + iptables -N natter 2> /dev/null + iptables -I INPUT -j natter 2> /dev/null + for i in $iptables_type + do + # iptables -A natter \ + # -p $i -m $i --dport $external_port \ + # -m comment --comment "nt-op-$id-$remarks" \ + # -j ACCEPT + # echo "iptables -A natter -p $i -m $i --dport $external_port -m comment --comment \"nt-op-$id-$remarks\" -j ACCEPT" >> ${include_file} + iptables -t nat -A PREROUTING \ + -p $i -m $i --dport $external_port \ + -m comment --comment "nt-dnat-$id-$remarks" \ + -j DNAT \ + --to-destination $internal_ip:$internal_port + echo "iptables -t nat -A PREROUTING -p $i -m $i --dport $external_port -m comment --comment \"nt-dnat-$id-$remarks\" -j DNAT --to-destination $internal_ip:$internal_port" >> ${include_file} + done ; unset i + ;; + esac + uci set natter.@ports[$u].external_port_tcp="WAIT" + uci set natter.@ports[$u].external_port_udp="WAIT" + uci commit natter + { + sleep 10 + external_port_tcp=$(grep "[INFO]" $log_file 2> /dev/null | grep TCP | egrep -o "[0-9]+" | awk 'END{print}') + external_port_udp=$(grep "[INFO]" $log_file 2> /dev/null | grep UDP | egrep -o "[0-9]+" | awk 'END{print}') + [ "${external_port_tcp}" ] || external_port_tcp="none" + [ "${external_port_udp}" ] || external_port_udp="none" + uci set natter.@ports[$u].external_port_tcp="$external_port_tcp" + uci set natter.@ports[$u].external_port_udp="$external_port_udp" + uci commit natter + } & + fi + for i in $port_list + do + unset $(echo $i) + done ; unset i + unset iptables_type internal_tcp internal_udp external_tcp external_udp external_port_tcp external_port_udp + done ; unset u + else + echo "Natter is disabled ..." + stop_service + fi +} + +stop_service() { + echo "Stopping Natter ..." + for i in $(ps -efww | egrep 'natter.py' | grep -v grep | grep -v $$ | awk '{print $1}') + do + kill -9 "$i" 2> /dev/null + done + iptables_remove_rule + rm /var/etc/natter.include 2> /dev/null + rm -r /var/etc/natter 2> /dev/null + rm -r /tmp/natter 2> /dev/null +} + +iptables_remove_rule() { + echo "Removing iptable rules ..." + iptables -D INPUT -j natter 2> /dev/null + iptables -F natter 2> /dev/null + iptables -X natter 2> /dev/null + iptables-save | grep -v 'nt-dnat' | iptables-restore +} + +stop() { + stop_service +} + +start() { + start_service +} + +restart() { + stop + start +} + +service_triggers() { + procd_add_reload_trigger "natter" +} + +uci_get_by_type() { + local ret=$(uci get natter.@$1[$2].$3 2>/dev/null) + echo ${ret:=$4} +} diff --git a/luci-app-natter/root/etc/uci-defaults/luci-natter b/luci-app-natter/root/etc/uci-defaults/luci-natter new file mode 100755 index 000000000..eb13c32e9 --- /dev/null +++ b/luci-app-natter/root/etc/uci-defaults/luci-natter @@ -0,0 +1,17 @@ +#!/bin/sh + +uci -q batch <<-EOF >/dev/null + delete ucitrack.@natter[-1] + add ucitrack natter + set ucitrack.@natter[-1].init=natter + commit ucitrack + + delete firewall.natter + set firewall.natter=include + set firewall.natter.type=script + set firewall.natter.path=/var/etc/natter.include + set firewall.natter.reload=1 + commit firewall +EOF + +exit 0 \ No newline at end of file diff --git a/luci-app-natter/root/usr/share/luci-app-natter/log.sh b/luci-app-natter/root/usr/share/luci-app-natter/log.sh new file mode 100755 index 000000000..4b37db7de --- /dev/null +++ b/luci-app-natter/root/usr/share/luci-app-natter/log.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +log_path=$(uci get natter.@base[0].log_path 2> /dev/null) + +for i in $(ls -1 ${log_path} | grep natter | grep .log) +do + case $1 in + print) + echo -e "\n======> $i <======" + tail -n 30 ${log_path}/$i 2> /dev/null + echo -e "======> END of $i <======" + ;; + del) + echo > ${log_path}/$i + ;; + esac +done + +exit 0 diff --git a/luci-app-natter/root/usr/share/luci-app-natter/natcheck.sh b/luci-app-natter/root/usr/share/luci-app-natter/natcheck.sh new file mode 100755 index 000000000..01d31a20d --- /dev/null +++ b/luci-app-natter/root/usr/share/luci-app-natter/natcheck.sh @@ -0,0 +1,18 @@ +#!/bin/sh + +# Check=$(python3 /usr/share/natter/natter.py --check-nat 2>&1 | grep -v "Checking" | grep 'NAT Type for') + +script_file='/usr/share/natter/natter.py' +tmp_path=$(uci get natter.@base[0].log_path) +[ ! "$tmp_path" ] && tmp_path=/tmp/natter + +mkdir -p $tmp_path +python3 $script_file --check-nat 2>&1 | grep -v "Checking" | grep 'NAT Type for' > $tmp_path/natter_nat_type.tmp +TCP=$(awk -F '[:]+' '/TCP/{print $2}' $tmp_path/natter_nat_type.tmp | sed 's/\[//g;s/\]//g') +UDP=$(awk -F '[:]+' '/UDP/{print $2}' $tmp_path/natter_nat_type.tmp | sed 's/\[//g;s/\]//g') +rm -f $tmp_path/natter_nat_type.tmp +[ ! "$TCP" ] && TCP="未知" +[ ! "$UDP" ] && UDP="未知" + +echo "TCP:$TCP" +echo "UDP:$UDP" diff --git a/luci-app-natter/root/usr/share/rpcd/acl.d/luci-app-natter.json b/luci-app-natter/root/usr/share/rpcd/acl.d/luci-app-natter.json new file mode 100644 index 000000000..fbe0d8a20 --- /dev/null +++ b/luci-app-natter/root/usr/share/rpcd/acl.d/luci-app-natter.json @@ -0,0 +1,11 @@ +{ + "luci-app-natter": { + "description": "Grant UCI access for luci-app-natter", + "read": { + "uci": [ "natter" ] + }, + "write": { + "uci": [ "natter" ] + } + } +} diff --git a/luci-app-natter2/Makefile b/luci-app-natter2/Makefile new file mode 100755 index 000000000..e7d3ebe96 --- /dev/null +++ b/luci-app-natter2/Makefile @@ -0,0 +1,15 @@ +# Copyright (C) 2020-2024 Hyy2001X + +include $(TOPDIR)/rules.mk + +PKG_NAME:=luci-app-natter2 +PKG_VERSION:=1.0 +PKG_RELEASE:=3 + +LUCI_TITLE:=LuCI Support for Natter v2.0.0-rc2 +LUCI_PKGARCH:=all +LUCI_DEPENDS:=+natter2 + +include $(TOPDIR)/feeds/luci/luci.mk + +# call BuildPackage - OpenWrt buildroot signature diff --git a/luci-app-natter2/README.md b/luci-app-natter2/README.md new file mode 100755 index 000000000..0af235387 --- /dev/null +++ b/luci-app-natter2/README.md @@ -0,0 +1,3 @@ +# luci-app-natter2 + +项目地址: https://github.com/MikeWang000000/Natter diff --git a/luci-app-natter2/luasrc/controller/natter2.lua b/luci-app-natter2/luasrc/controller/natter2.lua new file mode 100755 index 000000000..b6b0dddd8 --- /dev/null +++ b/luci-app-natter2/luasrc/controller/natter2.lua @@ -0,0 +1,21 @@ +module("luci.controller.natter2",package.seeall) + +function index() + if not nixio.fs.access("/etc/config/natter2")then + return + end + entry({"admin","network","natter2"},alias("admin","network","natter2","base"),_("Natter v2"),99).dependent=true + entry({"admin","network","natter2","base"},cbi("natter2/base"),_("Base Settings"),10).leaf=true + entry({"admin","network","natter2","instances"},cbi("natter2/instances")).leaf=true + entry({"admin","network","natter2","log"},form("natter2/log"),_("Log"),20).leaf=true + entry({"admin","network","natter2","print_log"},call("print_log")).leaf=true + entry({"admin","network","natter2","del_log"},call("del_log")).leaf=true +end + +function print_log() + luci.http.write(luci.sys.exec("sh /usr/share/luci-app-natter2/log.sh print")) +end + +function del_log() + luci.http.write(luci.sys.exec("sh /usr/share/luci-app-natter2/log.sh del")) +end diff --git a/luci-app-natter2/luasrc/model/cbi/natter2/base.lua b/luci-app-natter2/luasrc/model/cbi/natter2/base.lua new file mode 100755 index 000000000..088d16fb1 --- /dev/null +++ b/luci-app-natter2/luasrc/model/cbi/natter2/base.lua @@ -0,0 +1,74 @@ +m = Map("natter2", translate("Natter v2"), +translate("Expose your port behind full-cone NAT to the Internet") +.. [[

]] +.. translate("Project") +.. [[]] +) + +s = m:section(TypedSection, "base") +s.addremove = false +s.anonymous = true + +local function check_file(e) + return luci.sys.exec('ls "%s" 2> /dev/null' % e) ~= "" and true or false +end + +enable = s:option(Flag, "enable", translate("Enable")) +enable.default = 0 + +if check_file("/tmp/natter2_nat_type") then + natter_nat_type_tcp = luci.sys.exec ("grep TCP /tmp/natter2_nat_type") + natter_nat_type_udp = luci.sys.exec ("grep UDP /tmp/natter2_nat_type") + nat_check = s:option (Button, "nat_check", translate("Check NAT Status"), translate("") .. "

" .. natter_nat_type_tcp .. "

" .. natter_nat_type_udp) +else + nat_check = s:option (Button, "nat_check", translate("Check NAT Status"), translate("Tips") + .. [[
]] .. translate("After clicking Exec button, please wait for the luci to refresh")) +end + +nat_check.inputtitle = translate("Exec") +nat_check.write = function() + luci.sys.call ("sh /usr/share/luci-app-natter2/nat-check.sh") + luci.http.redirect(luci.dispatcher.build_url("admin", "network", "natter2", "base")) +end + +tmp_path = s:option(Value, "tmp_path", translate("Tmp Path")) +tmp_path.default = "/tmp/natter2" +tmp_path.placeholder = "/tmp/natter2" +tmp_path.rmempty = false + +s = m:section(TypedSection, "instances", translate("Instances"), translate("Setting up multiple instances")) +s.anonymous = true +s.addremove = true +s.template = "cbi/tblsection" +s.extedit = luci.dispatcher.build_url("admin", "network", "natter2", "instances", "%s") +function s.create(...) + local e=TypedSection.create(...) + if e then + luci.http.redirect(s.extedit%e) + return + end +end + +enable_instance = s:option(Flag, "enable_instance", translate("Enable")) +enable_instance.default = 1 +enable_instance.width = "5%" + +remark = s:option(DummyValue,"remark",translate("Remark")) +remark.width = "5%" + +protocol = s:option(DummyValue,"protocol",translate("Protocol")) +remark.width = "5%" + +tmp_public_port = s:option(DummyValue, "tmp_public_port", translate("Public Port")) +remark.width = "5%" + +target_address = s:option(DummyValue, "target_address", translate("Target Address")) +remark.width = "5%" + +target_port = s:option(DummyValue, "target_port", translate("Target Port")) +remark.width = "5%" + +notify_path = s:option(DummyValue, "notify_path", translate("Notify Script Path")) +remark.width = "5%" + +return m diff --git a/luci-app-natter2/luasrc/model/cbi/natter2/instances.lua b/luci-app-natter2/luasrc/model/cbi/natter2/instances.lua new file mode 100755 index 000000000..01d4b51f4 --- /dev/null +++ b/luci-app-natter2/luasrc/model/cbi/natter2/instances.lua @@ -0,0 +1,119 @@ +m = Map("natter2", translate("Instances Settings"), +translate("") +.. [[]] +.. translate("Instructions") +.. [[]]) +m.redirect = luci.dispatcher.build_url("admin", "network", "natter2") + +s = m:section(NamedSection, arg[1], "instances", "") +s.addremove = false +s.dynamic = false + +local function check_binary(e) + return luci.sys.exec('which "%s" 2> /dev/null' % e) ~= "" and true or false +end + +enable_instance = s:option(Flag, "enable_instance", translate("Enable")) + +local e = luci.sys.exec("cut -d '-' -f1 /proc/sys/kernel/random/uuid 2> /dev/null") +id = s:option(Value, "id", translate("ID")) +id.default = e + +remark = s:option(Value, "remark", translate("Remark")) +remark.rmempty=false + +protocol = s:option(ListValue, "protocol", translate("Protocol")) +protocol:value('tcp', translate("TCP")) +protocol:value('udp', translate("UDP")) +protocol.default = 'tcp' + +enable_stun_server = s:option(Flag, "enable_stun_server", translate("Enable Stun Server"), translate("Using customized STUN server")) +stun_server = s:option(DynamicList, "stun_server", translate("STUN Server")) +stun_server.rmempty = true +stun_server:depends({enable_stun_server = "1"}) + +enable_keepalive_server = s:option(Flag, "enable_keepalive_server", translate("Enable Keepalive Server"), translate("Using customized Keepalive server")) +keepalive_server = s:option(Value, "keepalive_server", translate("Keepalive Server")) +keepalive_server.rmempty = true +keepalive_server:depends({enable_keepalive_server = "1"}) + +interval = s:option(Value, "interval", translate("Interval (Seconds)"), translate("The number of seconds between keepalive")) +interval.default = 15 +interval.datatype = "uinteger" + +enable_bonding = s:option(Flag, "enable_bonding", translate("Enable Bonding Options"), translate("Usually there is no need to enable binding")) +enable_bonding.rmempty = true +bonding_interface = s:option(Value, "bonding_interface", translate("Bonding Interface")) +bonding_interface.rmempty = true +bonding_interface.default = '0.0.0.0' +bonding_interface:depends({enable_bonding = "1"}) + +bonding_port = s:option(Value, "bonding_port", translate("Bonding Port")) +bonding_port.rmempty = true +bonding_port.default = '0' +bonding_port:depends({enable_bonding = "1"}) + +enable_forwarding = s:option(Flag, "enable_forwarding", translate("Enable Forwarding Options"), translate("Forwarding to internal devices")) + +forwarding_method = s:option(ListValue, "forwarding_method", translate("Forwarding Method"), +translate("") +.. [[]] +.. translate("Instructions for forwarding method") +.. [[]]) +forwarding_method:value('socket', translate("socket (Not Recommended)")) +if check_binary("iptables") then + forwarding_method:value('iptables', translate("iptables (Recommended)")) +end +if check_binary("nftables") then + forwarding_method:value('nftables', translate("nftables (Recommended)")) +end +if check_binary("socat") then + forwarding_method:value('socat', translate("socat")) +end +if check_binary("gost") then + forwarding_method:value('gost', translate("gost")) +end +forwarding_method.default = 'socket' +forwarding_method:depends({enable_forwarding = "1"}) + +target_address = s:option(Value, "target_address", translate("Target Address")) +target_address.datatype = "ipmask4" +luci.sys.net.ipv4_hints( +function(ip, name) + target_address:value(ip, "%s (%s)" %{ ip, name }) +end) +target_address:depends({enable_forwarding = "1"}) + +target_port = s:option(Value, "target_port", translate("Target Port")) +target_port.datatype = "port" +target_port:depends({enable_forwarding = "1"}) + +enable_forwarding_retry = s:option(Flag, "enable_forwarding_retry", translate("Enable Forwarding Retry"), translate("Retry until the target port is open")) +enable_forwarding_retry:depends({enable_forwarding = "1"}) +enable_forwarding_retry.default = 1 +enable_forwarding_retry.rmempty = false + +enable_quit = s:option(Flag, "enable_quit", translate("Enable Quit"), translate("Exit immediately when the mapping address changes")) +enable_quit.default = "0" + +delay = s:option(Value,"delay", translate("Start delay (Seconds)"), translate("Time to wait before starting this instance")) +delay.default = 0 +delay.datatype = "uinteger" +delay.rmempty = false + +log_level = s:option(ListValue, "log_level", translate("Log Level")) +log_level:value('normal', translate("Normal")) +log_level:value('verbose', translate("Verbose")) + +enable_notify = s:option(Flag,"enable_notify", translate("Enable Notify Script")) +enable_notify.rmempty = false +notify_path = s:option(Value, "notify_path", translate("Notify Script Path"), +translate("") +.. [[]] +.. translate("Instructions for using the notification script") +.. [[]]) +notify_path.rmempty = true +notify_path.default = "/usr/share/luci-app-natter2/notify-example.sh" +notify_path:depends({enable_notify = "1"}) + +return m diff --git a/luci-app-natter2/luasrc/model/cbi/natter2/log.lua b/luci-app-natter2/luasrc/model/cbi/natter2/log.lua new file mode 100755 index 000000000..24eecdf7e --- /dev/null +++ b/luci-app-natter2/luasrc/model/cbi/natter2/log.lua @@ -0,0 +1,6 @@ +log = SimpleForm("natter2") +log.reset = false +log.submit = false +log:append(Template("natter2/natter_log")) + +return log diff --git a/luci-app-natter2/luasrc/view/natter2/natter_log.htm b/luci-app-natter2/luasrc/view/natter2/natter_log.htm new file mode 100755 index 000000000..8ea55f4a1 --- /dev/null +++ b/luci-app-natter2/luasrc/view/natter2/natter_log.htm @@ -0,0 +1,29 @@ + + +
+ + +
diff --git a/luci-app-natter2/po/zh-cn/natter2.po b/luci-app-natter2/po/zh-cn/natter2.po new file mode 100755 index 000000000..0d85afbe8 --- /dev/null +++ b/luci-app-natter2/po/zh-cn/natter2.po @@ -0,0 +1,149 @@ +msgid "Natter v2" +msgstr "Natter v2 测试版" + +msgid "Base Settings" +msgstr "基础设置" + +msgid "Log" +msgstr "日志" + +msgid "Expose your port behind full-cone NAT to the Internet" +msgstr "将 FullCone NAT (NAT 1) 后的端口,打洞暴露至互联网" + +msgid "Project" +msgstr "项目地址" + +msgid "Enable" +msgstr "启用" + +msgid "Check NAT Status" +msgstr "NAT 类型检测" + +msgid "Proxy programs may affect NAT types" +msgstr "代理程序可能会影响 NAT 类型" + +msgid "Tmp Path" +msgstr "临时路径" + +msgid "Instances" +msgstr "实例" + +msgid "Setting up multiple instances" +msgstr "设置多个实例" + +msgid "Remark" +msgstr "备注" + +msgid "Public Port" +msgstr "公开端口" + +msgid "Target Address" +msgstr "目标地址" + +msgid "Target Port" +msgstr "目标端口" + +msgid "Notify Script Path" +msgstr "通知脚本路径" + +msgid "Enable Stun Server" +msgstr "自定义 STUN 服务器" + +msgid "STUN Server" +msgstr "STUN 服务器" + +msgid "Enable Keepalive Server" +msgstr "自定义保活服务器" + +msgid "Keepalive Server" +msgstr "保活服务器" + +msgid "Interval (Seconds)" +msgstr "保活间隔(秒)" + +msgid "The number of seconds between keepalive" +msgstr "每次保活的间隔秒数" + +msgid "Enable Bonding Options" +msgstr "启用绑定" + +msgid "Bonding Interface" +msgstr "绑定接口" + +msgid "Bonding Port" +msgstr "绑定端口" + +msgid "Enable Forwarding Options" +msgstr "启用转发" + +msgid "Forwarding to internal devices" +msgstr "转发至内部设备" + +msgid "Forwarding Method" +msgstr "转发方式" + +msgid "Instructions for forwarding method" +msgstr "转发方式说明" + +msgid "socket (Not Recommended)" +msgstr "Socket(内置, 不推荐)" + +msgid "iptables (Recommended)" +msgstr "iptables(防火墙, 推荐)" + +msgid "nftables (Recommended)" +msgstr "nftables(防火墙, 推荐)" + +msgid "Target Address" +msgstr "目标地址" + +msgid "Target Port" +msgstr "目标端口" + +msgid "Enable Forwarding Retry" +msgstr "自动重试" + +msgid "Retry until the target port is open" +msgstr "重试直至目标端口开放" + +msgid "Enable Quit" +msgstr "自动退出" + +msgid "Exit immediately when the mapping address changes" +msgstr "映射地址改变时立即退出" + +msgid "Time to wait before starting this instance" +msgstr "启动该实例前等待的时间" + +msgid "Enable Notify Script" +msgstr "启用通知脚本" + +msgid "Instructions for using the notification script" +msgstr "通知脚本说明" + +msgid "Start delay (Seconds)" +msgstr "启动延时(秒)" + +msgid "Log Level" +msgstr "日志级别" + +msgid "Normal" +msgstr "正常" + +msgid "Verbose" +msgstr "详细" + +msgid "Instructions" +msgstr "使用说明" + +msgid "Using customized STUN server" +msgstr "使用自定义的 STUN 服务器" + +msgid "Using customized Keepalive server" +msgstr "使用自定义的保活服务器" + +msgid "Tips" +msgstr "提示" + +msgid "After clicking Exec button, please wait for the luci to refresh" +msgstr "点击执行后,请耐心等待页面自动刷新" diff --git a/luci-app-natter2/po/zh_Hans/natter2.po b/luci-app-natter2/po/zh_Hans/natter2.po new file mode 100755 index 000000000..0d85afbe8 --- /dev/null +++ b/luci-app-natter2/po/zh_Hans/natter2.po @@ -0,0 +1,149 @@ +msgid "Natter v2" +msgstr "Natter v2 测试版" + +msgid "Base Settings" +msgstr "基础设置" + +msgid "Log" +msgstr "日志" + +msgid "Expose your port behind full-cone NAT to the Internet" +msgstr "将 FullCone NAT (NAT 1) 后的端口,打洞暴露至互联网" + +msgid "Project" +msgstr "项目地址" + +msgid "Enable" +msgstr "启用" + +msgid "Check NAT Status" +msgstr "NAT 类型检测" + +msgid "Proxy programs may affect NAT types" +msgstr "代理程序可能会影响 NAT 类型" + +msgid "Tmp Path" +msgstr "临时路径" + +msgid "Instances" +msgstr "实例" + +msgid "Setting up multiple instances" +msgstr "设置多个实例" + +msgid "Remark" +msgstr "备注" + +msgid "Public Port" +msgstr "公开端口" + +msgid "Target Address" +msgstr "目标地址" + +msgid "Target Port" +msgstr "目标端口" + +msgid "Notify Script Path" +msgstr "通知脚本路径" + +msgid "Enable Stun Server" +msgstr "自定义 STUN 服务器" + +msgid "STUN Server" +msgstr "STUN 服务器" + +msgid "Enable Keepalive Server" +msgstr "自定义保活服务器" + +msgid "Keepalive Server" +msgstr "保活服务器" + +msgid "Interval (Seconds)" +msgstr "保活间隔(秒)" + +msgid "The number of seconds between keepalive" +msgstr "每次保活的间隔秒数" + +msgid "Enable Bonding Options" +msgstr "启用绑定" + +msgid "Bonding Interface" +msgstr "绑定接口" + +msgid "Bonding Port" +msgstr "绑定端口" + +msgid "Enable Forwarding Options" +msgstr "启用转发" + +msgid "Forwarding to internal devices" +msgstr "转发至内部设备" + +msgid "Forwarding Method" +msgstr "转发方式" + +msgid "Instructions for forwarding method" +msgstr "转发方式说明" + +msgid "socket (Not Recommended)" +msgstr "Socket(内置, 不推荐)" + +msgid "iptables (Recommended)" +msgstr "iptables(防火墙, 推荐)" + +msgid "nftables (Recommended)" +msgstr "nftables(防火墙, 推荐)" + +msgid "Target Address" +msgstr "目标地址" + +msgid "Target Port" +msgstr "目标端口" + +msgid "Enable Forwarding Retry" +msgstr "自动重试" + +msgid "Retry until the target port is open" +msgstr "重试直至目标端口开放" + +msgid "Enable Quit" +msgstr "自动退出" + +msgid "Exit immediately when the mapping address changes" +msgstr "映射地址改变时立即退出" + +msgid "Time to wait before starting this instance" +msgstr "启动该实例前等待的时间" + +msgid "Enable Notify Script" +msgstr "启用通知脚本" + +msgid "Instructions for using the notification script" +msgstr "通知脚本说明" + +msgid "Start delay (Seconds)" +msgstr "启动延时(秒)" + +msgid "Log Level" +msgstr "日志级别" + +msgid "Normal" +msgstr "正常" + +msgid "Verbose" +msgstr "详细" + +msgid "Instructions" +msgstr "使用说明" + +msgid "Using customized STUN server" +msgstr "使用自定义的 STUN 服务器" + +msgid "Using customized Keepalive server" +msgstr "使用自定义的保活服务器" + +msgid "Tips" +msgstr "提示" + +msgid "After clicking Exec button, please wait for the luci to refresh" +msgstr "点击执行后,请耐心等待页面自动刷新" diff --git a/luci-app-natter2/root/etc/config/natter2 b/luci-app-natter2/root/etc/config/natter2 new file mode 100755 index 000000000..862201da5 --- /dev/null +++ b/luci-app-natter2/root/etc/config/natter2 @@ -0,0 +1,4 @@ + +config base + option tmp_path '/tmp/natter2' + option enable '0' diff --git a/luci-app-natter2/root/etc/init.d/natter2 b/luci-app-natter2/root/etc/init.d/natter2 new file mode 100755 index 000000000..5911efc7c --- /dev/null +++ b/luci-app-natter2/root/etc/init.d/natter2 @@ -0,0 +1,126 @@ +#!/bin/sh /etc/rc.common + +START=98 +script_path="/usr/share/natter2/natter.py" + +start_service() { + local basic_list="enable tmp_path" + local instance_list="enable_instance id remark protocol enable_stun_server \ + stun_server enable_keepalive_server keepalive_server \ + interval enable_bonding bonding_interface bonding_port \ + enable_forwarding forwarding_method target_address target_port \ + enable_forwarding_retry enable_quit delay log_level enable_notify \ + notify_path" + for i in $basic_list + do + local eval $i="$(uci_get_by_type base 0 $i)" + done ; unset i + if [ "$enable" == 1 ] + then + mkdir -p ${tmp_path} + mkdir -p /var/etc/natter2 + for u in $(seq 0 $(($(uci show natter2 2> /dev/null | egrep '@instances\[[0-9]\]+=instances' | wc -l) - 1))) + do + for i in $instance_list + do + local eval $i="$(uci_get_by_type instances $u $i)" + # echo "$i : $(uci_get_by_type instances $u $i)" + done ; unset i + + [ "$enable_instance" != 1 ] && continue + script_command="" + + [ "$protocol" == udp ] && script_command="$script_command -u" + + if [ "$enable_stun_server" == 1 ] + then + for i in $stun_server + do + script_command="$script_command -s $i" + done ; unset i + fi + + [ "$enable_keepalive_server" == 1 ] && script_command="$script_command -h $keepalive_server" + [ "$interval" ] && script_command="$script_command -k $interval" + + if [ "$enable_bonding" == 1 ] + then + [ "$bonding_interface" ] && script_command="$script_command -i $bonding_interface" + [ "$bonding_port" ] && script_command="$script_command -b $bonding_port" + fi + + if [ "$enable_forwarding" == 1 ] + then + [ "$forwarding_method" ] && script_command="$script_command -m $forwarding_method" + [ "$target_address" ] && script_command="$script_command -t $target_address" + [ "$target_port" ] && script_command="$script_command -p $target_port" + [ "$enable_forwarding_retry" == 1 ] && script_command="$script_command -r" + fi + + [ "$enable_quit" == 1 ] && script_command="$script_command -q" + + [ "$log_level" == "verbose" ] && script_command="$script_command -v" + + if [ "$enable_notify" == 1 ] + then + echo "$notify_path" > /var/etc/natter2/${id}-notify + var_file=/var/etc/natter2/${id}-${u}-1 + else + var_file=/var/etc/natter2/${id}-${u}-0 + fi + + log_file=${tmp_path}/natter2-${id}.log + + cp -a /usr/share/luci-app-natter2/notify-base.sh $var_file + chmod +x $var_file + + # echo "log_file: $log_file" + # echo "var_file: $var_file" + + sleep $delay + for i in $(ps -efww | grep "$script_path" | grep -v grep | grep -v $$ | grep "$id" | awk '{print $1}') + do + kill -9 "$i" 2> /dev/null + done + + nohup $(command -v python) "$script_path" $script_command -e $var_file > "$log_file" 2>&1 & + + for i in $instance_list + do + unset $(echo $i) + done ; unset i + unset script_command + done ; unset u + else + stop_service + fi +} + +stop_service() { + echo "Stopping Natter2 ..." + for i in $(ps -efww | grep "$script_path" | grep -v grep | grep -v $$ | awk '{print $1}') + do + kill -9 "$i" 2> /dev/null + done + rm -r "$tmp_path" 2> /dev/null + for u in $(seq 0 $(($(uci show natter2 2> /dev/null | egrep '@instances\[[0-9]\]+=instances' | wc -l) - 1))) + do + uci set natter2.@instances[$u].tmp_public_port="" + uci commit natter2 + done + rm -f /tmp/natter2_nat_type +} + +restart() { + stop_service + start_service +} + +service_triggers() { + procd_add_reload_trigger "natter2" +} + +uci_get_by_type() { + local ret=$(uci get natter2.@$1[$2].$3 2>/dev/null) + echo ${ret:=$4} +} diff --git a/luci-app-natter2/root/etc/uci-defaults/luci-natter2 b/luci-app-natter2/root/etc/uci-defaults/luci-natter2 new file mode 100755 index 000000000..1fac60d8e --- /dev/null +++ b/luci-app-natter2/root/etc/uci-defaults/luci-natter2 @@ -0,0 +1,10 @@ +#!/bin/sh + +uci -q batch <<-EOF >/dev/null + delete ucitrack.@natter2[-1] + add ucitrack natter2 + set ucitrack.@natter2[-1].init=natter2 + commit ucitrack +EOF + +exit 0 diff --git a/luci-app-natter2/root/usr/share/luci-app-natter2/log.sh b/luci-app-natter2/root/usr/share/luci-app-natter2/log.sh new file mode 100755 index 000000000..240a8ada3 --- /dev/null +++ b/luci-app-natter2/root/usr/share/luci-app-natter2/log.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +log_path=$(uci get natter2.@base[0].tmp_path 2> /dev/null) + +for i in $(ls -1 ${log_path} | grep natter2 | grep .log) +do + case $1 in + print) + echo -e "\n======> $i <======" + tail -n 30 ${log_path}/$i 2> /dev/null + echo -e "======> END of $i <======" + ;; + del) + echo > ${log_path}/$i + ;; + esac +done + +exit 0 diff --git a/luci-app-natter2/root/usr/share/luci-app-natter2/nat-check.sh b/luci-app-natter2/root/usr/share/luci-app-natter2/nat-check.sh new file mode 100755 index 000000000..d82e10cb6 --- /dev/null +++ b/luci-app-natter2/root/usr/share/luci-app-natter2/nat-check.sh @@ -0,0 +1,39 @@ +#!/bin/sh + +script_file='/usr/share/natter2/natter-check/natter-check.py' +natter2_nat_type_file="/tmp/natter2_nat_type" +tmp_natter2_nat_type_file="/tmp/tmp_natter2_nat_type" + +rm -f $natter2_nat_type_file +rm -f $tmp_natter2_nat_type_file + +$(which python) $script_file | egrep 'Checking TCP|Checking UDP' > $tmp_natter2_nat_type_file +TCP=$(awk -F '[:]+' '/TCP/{print $2}' $tmp_natter2_nat_type_file | sed 's/\[//g;s/\]//g') +UDP=$(awk -F '[:]+' '/UDP/{print $2}' $tmp_natter2_nat_type_file | sed 's/\[//g;s/\]//g') + +[ ! "$TCP" ] && TCP="未知" +[ ! "$UDP" ] && UDP="未知" + +function NAT_Type() { + case $1 in + 0) + echo "Public Network" + ;; + 1) + echo "Full Cone" + ;; + 2) + echo "Restricted Cone" + ;; + 3) + echo "Port Restricted Cone" + ;; + 4) + echo "Symmetric" + ;; + esac +} +echo "TCP: NAT $TCP | $(NAT_Type $TCP)" > $natter2_nat_type_file +echo "UDP: NAT $UDP | $(NAT_Type $UDP)" >> $natter2_nat_type_file + +rm -f $tmp_natter2_nat_type_file \ No newline at end of file diff --git a/luci-app-natter2/root/usr/share/luci-app-natter2/notify-base.sh b/luci-app-natter2/root/usr/share/luci-app-natter2/notify-base.sh new file mode 100755 index 000000000..687e0a174 --- /dev/null +++ b/luci-app-natter2/root/usr/share/luci-app-natter2/notify-base.sh @@ -0,0 +1,28 @@ +#!/bin/sh + +protocol="$1"; private_ip="$2"; private_port="$3"; public_ip="$4"; public_port="$5" + +script_name=$(basename $0) +script_path=$(dirname $0) +instance_id=$(echo $script_name | cut -d '-' -f1) +instance_num=$(echo $script_name | cut -d '-' -f2) +instance_isnotify=$(echo $script_name | cut -d '-' -f3) + +# echo script_path:$script_path +# echo script_name:$script_name +# echo instance_id:$instance_id +# echo instance_num:$instance_num +# echo instance_isnotify:$instance_isnotify + +uci set natter2.@instances[$instance_num].tmp_public_port="$public_port" +uci commit natter2 + +if [ "$instance_isnotify" == 1 ] +then + notify_path=$(cat $script_path/${instance_id}-notify) + if [ -f "${notify_path}" ] + then + chmod +x ${notify_path} + ${notify_path} $1 $2 $3 $4 $5 + fi +fi diff --git a/luci-app-natter2/root/usr/share/luci-app-natter2/notify-example.sh b/luci-app-natter2/root/usr/share/luci-app-natter2/notify-example.sh new file mode 100755 index 000000000..d9f3c22a9 --- /dev/null +++ b/luci-app-natter2/root/usr/share/luci-app-natter2/notify-example.sh @@ -0,0 +1,12 @@ +#!/bin/sh + +# 参数序号 参数说明 参数格式 +# 1 传输层协议 tcp, udp 二者之一 +# 2 内部 IP 点分十进制 IPv4 地址 +# 3 内部端口 1 - 65535 的整数 +# 4 外部 IP 点分十进制 IPv4 地址 +# 5 外部端口 1 - 65535 的整数 + +protocol="$1"; private_ip="$2"; private_port="$3"; public_ip="$4"; public_port="$5" + +echo $1 $2 $3 $4 $5 diff --git a/luci-app-natter2/root/usr/share/rpcd/acl.d/luci-app-natter2.json b/luci-app-natter2/root/usr/share/rpcd/acl.d/luci-app-natter2.json new file mode 100644 index 000000000..db0c15123 --- /dev/null +++ b/luci-app-natter2/root/usr/share/rpcd/acl.d/luci-app-natter2.json @@ -0,0 +1,11 @@ +{ + "luci-app-natter2": { + "description": "Grant UCI access for luci-app-natter2", + "read": { + "uci": [ "natter2" ] + }, + "write": { + "uci": [ "natter2" ] + } + } +} diff --git a/luci-app-npc/Makefile b/luci-app-npc/Makefile new file mode 100755 index 000000000..9dfa4f6cb --- /dev/null +++ b/luci-app-npc/Makefile @@ -0,0 +1,14 @@ +# Copyright (C) 2008-2014 The LuCI Team +# Copyright (C) 2020-2021 Hyy2001X + +include $(TOPDIR)/rules.mk + +LUCI_TITLE:=LuCI support for NPS Client(NPC) +LUCI_DEPENDS:=+npc +LUCI_PKGARCH:=all +PKG_VERSION:=1.3 +PKG_RELEASE:=2 + +include $(TOPDIR)/feeds/luci/luci.mk + +# call BuildPackage - OpenWrt buildroot signature diff --git a/luci-app-npc/README.md b/luci-app-npc/README.md new file mode 100755 index 000000000..ac6e6d0c9 --- /dev/null +++ b/luci-app-npc/README.md @@ -0,0 +1,11 @@ +# luci-app-npc + +## 修改内容 + + - 解除原有服务端仅 IPv4 地址的限制 + + - 添加[速度限制] [流量限制] [最大连接数] 等多个选项 + +## Based on + + - [lede](https://github.com/coolsnowwolf/lede/tree/master/package/lean/luci-app-nps) diff --git a/luci-app-npc/luasrc/controller/npc.lua b/luci-app-npc/luasrc/controller/npc.lua new file mode 100755 index 000000000..54f1cf79a --- /dev/null +++ b/luci-app-npc/luasrc/controller/npc.lua @@ -0,0 +1,17 @@ +module("luci.controller.npc",package.seeall) + +function index() + if not nixio.fs.access("/etc/config/npc") then + return + end + + entry({"admin", "services", "npc"}, cbi("npc"), _("NPS Client"), 99).dependent = true + entry({"admin", "services", "npc", "status"}, call("act_status")).leaf = true +end + +function act_status() + local e = {} + e.running = luci.sys.call("pgrep npc > /dev/null") == 0 + luci.http.prepare_content("application/json") + luci.http.write_json(e) +end diff --git a/luci-app-npc/luasrc/model/cbi/npc.lua b/luci-app-npc/luasrc/model/cbi/npc.lua new file mode 100755 index 000000000..5e8c45de5 --- /dev/null +++ b/luci-app-npc/luasrc/model/cbi/npc.lua @@ -0,0 +1,50 @@ +m = Map("npc", translate("NPS Client"), translate("Nps is a fast reverse proxy to help you expose a local server behind a NAT or firewall to the internet.")) + +m:section(SimpleSection).template = "npc/npc_status" + +s = m:section(TypedSection,"npc") +s.addremove = false +s.anonymous = true + +enable = s:option(Flag, "enable", translate("Enable")) +enable.rmempty = false +enable.default = "0" + +server = s:option(Value, "server_addr", translate("Server Address"), translate("IPv4 address or Domain Name")) +server.rmempty = false + +port = s:option(Value, "server_port", translate("Port")) +port.datatype = "port" +port.default = "8024" +port.rmempty = false + +vkey = s:option(Value, "vkey", translate("vkey")) +vkey.password = true +vkey.rmempty = false + +protocol = s:option(ListValue, "protocol", translate("Protocol Type")) +protocol.default = "tcp" +protocol:value("tcp", translate("TCP Protocol")) +protocol:value("kcp", translate("KCP Protocol")) + +max_conn = s:option(Value, "max_conn", translate("Max Connection Limit"), translate("Maximum number of connections (Not necessary)")) +max_conn.optional = true +max_conn.rmempty = true + +rate_limit = s:option(Value, "rate_limit", translate("Rate Limit"), translate("Client rate limit (Not necessary)")) +rate_limit.optional = true +rate_limit.rmempty = true + +flow_limit = s:option(Value, "flow_limit", translate("Flow Limit"), translate("Client flow limit (Not necessary)")) +flow_limit.optional = true +flow_limit.rmempty = true + +compress = s:option(Flag, "compress", translate("Enable Compression"), translate("The contents will be compressed to speed up the traffic forwarding speed, but this will consume some additional cpu resources.")) +compress.default = "0" +compress.rmempty = false + +crypt = s:option(Flag, "crypt", translate("Enable Encryption"), translate("Encrypted the communication between Npc and Nps, will effectively prevent the traffic intercepted.")) +crypt.default = "0" +crypt.rmempty = false + +return m diff --git a/luci-app-npc/luasrc/view/npc/npc_status.htm b/luci-app-npc/luasrc/view/npc/npc_status.htm new file mode 100755 index 000000000..55897428e --- /dev/null +++ b/luci-app-npc/luasrc/view/npc/npc_status.htm @@ -0,0 +1,22 @@ + + +
+

+ <%:Collecting data...%> +

+
diff --git a/luci-app-npc/po/zh-cn/npc.po b/luci-app-npc/po/zh-cn/npc.po new file mode 100755 index 000000000..c039622dd --- /dev/null +++ b/luci-app-npc/po/zh-cn/npc.po @@ -0,0 +1,56 @@ +msgid "NPS Client" +msgstr "NPS 内网穿透客户端" + +msgid "Nps is a fast reverse proxy to help you expose a local server behind a NAT or firewall to the internet." +msgstr "NPS 是一种快速反向代理,可帮助您将 NAT 或防火墙后的本地服务器公开到 Internet" + +msgid "IPv4 address or Domain Name" +msgstr "服务器域名或 IPv4 地址" + +msgid "vkey" +msgstr "唯一验证密钥(vkey)" + +msgid "Enable Compression" +msgstr "压缩传输" + +msgid "Enable Encryption" +msgstr "加密传输" + +msgid "The contents will be compressed to speed up the traffic forwarding speed, but this will consume some additional cpu resources." +msgstr "启用压缩传输内容会加快流量转发速度,但是会额外消耗 CPU 资源" + +msgid "Encrypted the communication between Npc and Nps, will effectively prevent the traffic intercepted." +msgstr "启用加密传输客户端与服务端之间的通信内容,会有效防止流量被拦截" + +msgid "Basic Setting" +msgstr "基本设置" + +msgid "Protocol Type" +msgstr "协议类型" + +msgid "Server Address" +msgstr "服务端地址" + +msgid "TCP Protocol" +msgstr "TCP" + +msgid "KCP Protocol" +msgstr "KCP" + +msgid "Max Connection Limit" +msgstr "连接数限制" + +msgid "Maximum number of connections (Not necessary)" +msgstr "最大连接数限制 (可选,非必须)" + +msgid "Rate Limit" +msgstr "速度限制" + +msgid "Client rate limit (Not necessary)" +msgstr "客户端速度限制 (可选,非必须)" + +msgid "Flow Limit" +msgstr "流量限制" + +msgid "Client flow limit (Not necessary)" +msgstr "客户端流量限制 (可选,非必须)" diff --git a/luci-app-npc/po/zh_Hans/npc.po b/luci-app-npc/po/zh_Hans/npc.po new file mode 100755 index 000000000..c039622dd --- /dev/null +++ b/luci-app-npc/po/zh_Hans/npc.po @@ -0,0 +1,56 @@ +msgid "NPS Client" +msgstr "NPS 内网穿透客户端" + +msgid "Nps is a fast reverse proxy to help you expose a local server behind a NAT or firewall to the internet." +msgstr "NPS 是一种快速反向代理,可帮助您将 NAT 或防火墙后的本地服务器公开到 Internet" + +msgid "IPv4 address or Domain Name" +msgstr "服务器域名或 IPv4 地址" + +msgid "vkey" +msgstr "唯一验证密钥(vkey)" + +msgid "Enable Compression" +msgstr "压缩传输" + +msgid "Enable Encryption" +msgstr "加密传输" + +msgid "The contents will be compressed to speed up the traffic forwarding speed, but this will consume some additional cpu resources." +msgstr "启用压缩传输内容会加快流量转发速度,但是会额外消耗 CPU 资源" + +msgid "Encrypted the communication between Npc and Nps, will effectively prevent the traffic intercepted." +msgstr "启用加密传输客户端与服务端之间的通信内容,会有效防止流量被拦截" + +msgid "Basic Setting" +msgstr "基本设置" + +msgid "Protocol Type" +msgstr "协议类型" + +msgid "Server Address" +msgstr "服务端地址" + +msgid "TCP Protocol" +msgstr "TCP" + +msgid "KCP Protocol" +msgstr "KCP" + +msgid "Max Connection Limit" +msgstr "连接数限制" + +msgid "Maximum number of connections (Not necessary)" +msgstr "最大连接数限制 (可选,非必须)" + +msgid "Rate Limit" +msgstr "速度限制" + +msgid "Client rate limit (Not necessary)" +msgstr "客户端速度限制 (可选,非必须)" + +msgid "Flow Limit" +msgstr "流量限制" + +msgid "Client flow limit (Not necessary)" +msgstr "客户端流量限制 (可选,非必须)" diff --git a/luci-app-npc/root/etc/config/npc b/luci-app-npc/root/etc/config/npc new file mode 100755 index 000000000..149367260 --- /dev/null +++ b/luci-app-npc/root/etc/config/npc @@ -0,0 +1,6 @@ + +config npc + option enable '0' + option server_addr '1.2.3.4' + option vkey 'abcdefg' + diff --git a/luci-app-npc/root/etc/init.d/npc b/luci-app-npc/root/etc/init.d/npc new file mode 100755 index 000000000..783340531 --- /dev/null +++ b/luci-app-npc/root/etc/init.d/npc @@ -0,0 +1,67 @@ +#!/bin/sh /etc/rc.common + +START=95 +USE_PROCD=1 +LOGGER="logger -t [NPC]" + +npc_Path="$(command -v npc)" +conf_Path="/tmp/etc/npc.conf" + +start_service() { + local basic_list="enable server_addr server_port protocol vkey max_conn rate_limit flow_limit compress crypt" + for i in $(echo $basic_list);do + local eval $i="$(uci_get_by_type npc 0 $i)" + done;unset i + + [ -s "$conf_Path" ] && rm -f $conf_Path + echo "[common]" > $conf_Path || { + ${LOGGER} "Failed to create config,exit ..." + exit 1 + } + echo "server_addr=${server_addr}:${server_port}" >> $conf_Path + echo "conn_type=${protocol}" >> $conf_Path + echo "vkey=${vkey}" >> $conf_Path + echo "auto_reconnection=true" >> $conf_Path + [ -n "$max_conn" ] && echo "max_conn=${max_conn}" >> $conf_Path + [ -n "$rate_limit" ] && echo "rate_limit=${rate_limit}" >> $conf_Path + [ -n "$flow_limit" ] && echo "flow_limit=${flow_limit}" >> $conf_Path + conf_write_bool compress $compress + conf_write_bool crypt $crypt + + if [ "$enable" = 1 ] + then + ${LOGGER} "Starting NPS Client(NPC) ..." + procd_open_instance + procd_set_param command $npc_Path -config=$conf_Path + procd_set_param respawn + procd_set_param stdout 1 + procd_set_param stderr 1 + procd_close_instance + else + ${LOGGER} "NPS Client(NPC) Service is now disabled ..." + fi +} + +stop_service() { + $LOGGER "Stopping NPS Client(NPC) ..." + rm -f $conf_Path +} + +service_triggers() { + procd_add_reload_trigger "npc" +} + +conf_write_bool() { + if [ "$2" == 0 ] + then + echo "$1=false" >> $conf_Path + else + echo "$1=true" >> $conf_Path + fi + return +} + +uci_get_by_type() { + local ret=$(uci get npc.@$1[$2].$3 2>/dev/null) + echo ${ret:=$4} +} diff --git a/luci-app-npc/root/etc/uci-defaults/luci-npc b/luci-app-npc/root/etc/uci-defaults/luci-npc new file mode 100755 index 000000000..8f6dc3789 --- /dev/null +++ b/luci-app-npc/root/etc/uci-defaults/luci-npc @@ -0,0 +1,11 @@ +#!/bin/sh + +uci -q batch <<-EOF >/dev/null + delete ucitrack.@npc[-1] + add ucitrack npc + set ucitrack.@npc[-1].init=npc + commit ucitrack +EOF + +rm -f /tmp/luci-indexcache +exit 0 diff --git a/luci-app-npc/root/usr/share/rpcd/acl.d/luci-app-npc.json b/luci-app-npc/root/usr/share/rpcd/acl.d/luci-app-npc.json new file mode 100644 index 000000000..a2412b294 --- /dev/null +++ b/luci-app-npc/root/usr/share/rpcd/acl.d/luci-app-npc.json @@ -0,0 +1,11 @@ +{ + "luci-app-npc": { + "description": "Grant UCI access for luci-app-npc", + "read": { + "uci": [ "npc" ] + }, + "write": { + "uci": [ "npc" ] + } + } +} diff --git a/luci-app-passwall/luasrc/passwall/util_sing-box.lua b/luci-app-passwall/luasrc/passwall/util_sing-box.lua index 22d8dcda0..15c460d5d 100644 --- a/luci-app-passwall/luasrc/passwall/util_sing-box.lua +++ b/luci-app-passwall/luasrc/passwall/util_sing-box.lua @@ -95,7 +95,7 @@ function gen_outbound(flag, node, tag, proxy_table) local relay_port = node.port new_port = get_new_port() local config_file = string.format("%s_%s_%s.json", flag, tag, new_port) - if tag and node_id and tag ~= node_id then + if tag and node_id and not tag:find(node_id) then config_file = string.format("%s_%s_%s_%s.json", flag, tag, node_id, new_port) end if run_socks_instance then diff --git a/luci-app-passwall/luasrc/passwall/util_xray.lua b/luci-app-passwall/luasrc/passwall/util_xray.lua index 744d2c33a..7017ba6dc 100644 --- a/luci-app-passwall/luasrc/passwall/util_xray.lua +++ b/luci-app-passwall/luasrc/passwall/util_xray.lua @@ -74,7 +74,7 @@ function gen_outbound(flag, node, tag, proxy_table) local relay_port = node.port new_port = get_new_port() local config_file = string.format("%s_%s_%s.json", flag, tag, new_port) - if tag and node_id and tag ~= node_id then + if tag and node_id and not tag:find(node_id) then config_file = string.format("%s_%s_%s_%s.json", flag, tag, node_id, new_port) end if run_socks_instance then diff --git a/luci-app-shutdown/Makefile b/luci-app-shutdown/Makefile new file mode 100755 index 000000000..2e6116e84 --- /dev/null +++ b/luci-app-shutdown/Makefile @@ -0,0 +1,13 @@ +# Copyright (C) 2020-2021 Hyy2001X + +include $(TOPDIR)/rules.mk + +LUCI_TITLE:=LuCI Support for Shutdown Target +PKG_NAME:=luci-app-shutdown +PKG_VERSION:=1.0 +PKG_RELEASE:=1 +LUCI_PKGARCH:=all + +include $(TOPDIR)/feeds/luci/luci.mk + +# call BuildPackage - OpenWrt buildroot signature diff --git a/luci-app-shutdown/README.md b/luci-app-shutdown/README.md new file mode 100755 index 000000000..4a77844c2 --- /dev/null +++ b/luci-app-shutdown/README.md @@ -0,0 +1,3 @@ +## luci-app-shutdown + +一键关闭/重启设备 diff --git a/luci-app-shutdown/luasrc/controller/shutdown.lua b/luci-app-shutdown/luasrc/controller/shutdown.lua new file mode 100755 index 000000000..09792f1a8 --- /dev/null +++ b/luci-app-shutdown/luasrc/controller/shutdown.lua @@ -0,0 +1,5 @@ +module("luci.controller.shutdown",package.seeall) + +function index() + entry({"admin", "system", "shutdown"}, cbi("shutdown"), _("Shutdown"),99) +end diff --git a/luci-app-shutdown/luasrc/model/cbi/shutdown.lua b/luci-app-shutdown/luasrc/model/cbi/shutdown.lua new file mode 100755 index 000000000..6a006ce24 --- /dev/null +++ b/luci-app-shutdown/luasrc/model/cbi/shutdown.lua @@ -0,0 +1,23 @@ +require("luci.sys") + +m = SimpleForm("shutdown", translate("Shutdown/Reboot"), + translate("Shut down / restart the system on your device")) + +s = m:section(SimpleSection) + +button_shutdown = s:option (Button, "button_shutdown", translate("Shutdown"), translatef("Please wait for the device to shut down")) +button_shutdown.inputtitle = translate ("Do shutdown") +button_shutdown.write = function() + luci.sys.call("sync && poweroff > /dev/null") +end + +button_reboot = s:option (Button, "button_reboot", translate("Reboot"), translatef("Please wait a minutes until the device restart")) +button_reboot.inputtitle = translate ("Do reboot") +button_reboot.write = function() + luci.sys.call("sync && reboot > /dev/null") +end + +m.reset = false +m.submit = false + +return m diff --git a/luci-app-shutdown/po/zh-cn/shutdown.po b/luci-app-shutdown/po/zh-cn/shutdown.po new file mode 100755 index 000000000..bb258f790 --- /dev/null +++ b/luci-app-shutdown/po/zh-cn/shutdown.po @@ -0,0 +1,23 @@ +msgid "Shutdown" +msgstr "关机" + +msgid "Do shutdown" +msgstr "执行关机" + +msgid "Do reboot" +msgstr "执行重启" + +msgid "Reboot" +msgstr "重启" + +msgid "Shutdown/Reboot" +msgstr "关机/重启" + +msgid "Shut down / restart the system on your device" +msgstr "一键关闭/重启您设备上的系统" + +msgid "Please wait a minutes until the device restart" +msgstr "点击后请耐心等待 1-2 分钟直至设备重启成功" + +msgid "Please wait for the device to shut down" +msgstr "点击后请耐心等待设备关闭" diff --git a/luci-app-shutdown/po/zh_Hans/shutdown.po b/luci-app-shutdown/po/zh_Hans/shutdown.po new file mode 100755 index 000000000..bb258f790 --- /dev/null +++ b/luci-app-shutdown/po/zh_Hans/shutdown.po @@ -0,0 +1,23 @@ +msgid "Shutdown" +msgstr "关机" + +msgid "Do shutdown" +msgstr "执行关机" + +msgid "Do reboot" +msgstr "执行重启" + +msgid "Reboot" +msgstr "重启" + +msgid "Shutdown/Reboot" +msgstr "关机/重启" + +msgid "Shut down / restart the system on your device" +msgstr "一键关闭/重启您设备上的系统" + +msgid "Please wait a minutes until the device restart" +msgstr "点击后请耐心等待 1-2 分钟直至设备重启成功" + +msgid "Please wait for the device to shut down" +msgstr "点击后请耐心等待设备关闭" diff --git a/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/advanced.lua b/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/advanced.lua index b57d43706..2cca52135 100644 --- a/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/advanced.lua +++ b/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/advanced.lua @@ -249,10 +249,9 @@ o = s:option(Flag, "adblock", translate("Enable adblock")) o.rmempty = false o = s:option(Value, "adblock_url", translate("adblock_url")) -o:value("https://raw.githubusercontent.com/neodevpro/neodevhost/master/lite_dnsmasq.conf", translate("NEO DEV HOST Lite")) -o:value("https://raw.githubusercontent.com/neodevpro/neodevhost/master/dnsmasq.conf", translate("NEO DEV HOST Full")) +o:value("https://raw.githubusercontent.com/neodevpro/neodevhost/master/dnsmasq.conf", translate("NEO DEV HOST")) o:value("https://anti-ad.net/anti-ad-for-dnsmasq.conf", translate("anti-AD")) -o.default = "https://raw.githubusercontent.com/neodevpro/neodevhost/master/lite_dnsmasq.conf" +o.default = "https://raw.githubusercontent.com/neodevpro/neodevhost/master/dnsmasq.conf" o:depends("adblock", "1") o.description = translate("Support AdGuardHome and DNSMASQ format list") diff --git a/luci-app-webd/Makefile b/luci-app-webd/Makefile new file mode 100755 index 000000000..ead8ff037 --- /dev/null +++ b/luci-app-webd/Makefile @@ -0,0 +1,24 @@ +# Copyright (C) 2020-2021 Hyy2001X + +include $(TOPDIR)/rules.mk + +PKG_NAME:=luci-app-webd +PKG_VERSION:=1.4 +PKG_RELEASE:=1 + +LUCI_TITLE:=LuCI support for Webd Netdisk +LUCI_PKGARCH:=all + +LUCI_DEPENDS:= \ + +PACKAGE_$(PKG_NAME)_INCLUDE_WEBD_BINARY:webd + +define Package/$(PKG_NAME)/config + +config PACKAGE_$(PKG_NAME)_INCLUDE_WEBD_BINARY + bool "Include webd Binary" + default y +endef + +include $(TOPDIR)/feeds/luci/luci.mk + +# call BuildPackage - OpenWrt buildroot signature diff --git a/luci-app-webd/README.md b/luci-app-webd/README.md new file mode 100644 index 000000000..eda3ec5cd --- /dev/null +++ b/luci-app-webd/README.md @@ -0,0 +1,3 @@ +# luci-app-webd + +项目地址: https://webd.cf/ diff --git a/luci-app-webd/luasrc/controller/webd.lua b/luci-app-webd/luasrc/controller/webd.lua new file mode 100755 index 000000000..65bf78d04 --- /dev/null +++ b/luci-app-webd/luasrc/controller/webd.lua @@ -0,0 +1,17 @@ +module("luci.controller.webd",package.seeall) + +function index() + if not nixio.fs.access("/etc/config/webd") then + return + end + + entry({"admin", "nas", "webd"}, cbi("webd"), _("Webd Netdisk"),99) + entry({"admin", "nas", "webd", "status"}, call("act_status")).leaf = true +end + +function act_status() + local e = {} + e.running = luci.sys.call("pgrep webd > /dev/null") == 0 + luci.http.prepare_content("application/json") + luci.http.write_json(e) +end diff --git a/luci-app-webd/luasrc/model/cbi/webd.lua b/luci-app-webd/luasrc/model/cbi/webd.lua new file mode 100755 index 000000000..9cd3f3dd6 --- /dev/null +++ b/luci-app-webd/luasrc/model/cbi/webd.lua @@ -0,0 +1,83 @@ +m = Map("webd", translate("Webd Netdisk"), +translate("Webd - A lightweight self hosted netdisk") +.. [[ ]] +.. translate("Official Website") +.. [[]] +) + +m:section(SimpleSection).template = "webd/webd_status" + +s = m:section(TypedSection, "webd", translate("Basic Settings"), translate("Set the basic settings of Webd")) +s.anonymous = true + +enable = s:option(Flag, "enable", translate("Enable")) +enable.default = 0 + +port = s:option(Value, "webd_port", translate("Listening Port")) +port.datatype = "port" +port.default = "9212" +port.rmempty = false + +enable_ipv6 = s:option(Flag, "enable_ipv6", translate("Listen IPv6"), translatef("Listen both IPv4 and IPv6 Address")) +enable_ipv6.default = 0 + +root = s:option(Value, "webd_root", translate("Local Directory"), translatef("Directory of Webd")) +root.default = "/mnt" +root.rmempty = false + +enable_recyclebin = s:option(Flag, "enable_recyclebin", translate("Recycle Bin"), translatef("Automatically create recycle bin directory")) +enable_recyclebin.default = 1 + +enable_anonymous = s:option(Flag, "enable_anonymous", translate("Enable Anonymous Access"), translatef("Anonymous access is allowed when enabled (Not Safe)")) +enable_anonymous.default = 0 + +anonymous_perm = s:option(MultiValue, "anonymous_perm", translate("Anonymous Permission")) +anonymous_perm:value("r", translate("Read files")) +anonymous_perm:value("l", translate("Obtain file list")) +anonymous_perm:value("u", translate("Upload files")) +anonymous_perm:value("m", translate("Remove files")) +anonymous_perm:value("S", translate("Show hidden files")) +anonymous_perm:value("D", translate("Append download attribute")) +anonymous_perm:value("T", translate("Play media")) +anonymous_perm:depends("enable_anonymous", "1") +anonymous_perm.description = translate("At least one permission must be choosed to allow anonymous access") + +webd_bin = s:option(Value, "webd_bin", translate("Binary Path"), translatef("Webd binary Path")) +webd_bin.default = "/usr/bin/webd" +webd_bin.rmempty = false + +webd_conf = s:option(Value, "webd_conf", translate("Config Path"), translatef("Webd config Path")) +webd_conf.default = "/etc/webd.conf" +webd_conf.rmempty = false + +s = m:section(TypedSection, "users", translate("User Settings"), translate("Set the username, password and permissions. Maximum for 2 accounts")) +s.anonymous = true +s.addremove = true +s.template = "cbi/tblsection" + +username = s:option(Value, "username", translate("Username")) +username.rmempty = false + +password = s:option(Value, "password", translate("Password")) +password.rmempty = false +password.password=false + +enable_read = s:option(Flag, "enable_read", translate("Read files")) +enable_read.default = 1 + +enable_read_list = s:option(Flag, "enable_read_list", translate("Obtain file list")) +enable_read_list.default = 1 + +enable_upload = s:option(Flag, "enable_upload", translate("Upload files")) +enable_upload.default = 1 + +enable_move = s:option(Flag, "enable_move", translate("Remove files")) +enable_move.default = 1 + +enable_showhide = s:option(Flag, "enable_showhide", translate("Show hidden files")) +enable_showhide.default = 0 + +enable_play = s:option(Flag, "enable_play", translate("Play media")) +enable_play.default = 1 + +return m diff --git a/luci-app-webd/luasrc/view/webd/webd_status.htm b/luci-app-webd/luasrc/view/webd/webd_status.htm new file mode 100755 index 000000000..2df3e910d --- /dev/null +++ b/luci-app-webd/luasrc/view/webd/webd_status.htm @@ -0,0 +1,27 @@ + + +
+

+ <%:Collecting data...%> +

+
\ No newline at end of file diff --git a/luci-app-webd/po/zh-cn/webd.po b/luci-app-webd/po/zh-cn/webd.po new file mode 100755 index 000000000..77f471b5f --- /dev/null +++ b/luci-app-webd/po/zh-cn/webd.po @@ -0,0 +1,83 @@ +msgid "Webd Netdisk" +msgstr "Webd 网盘" + +msgid "Official Website" +msgstr "官网" + +msgid "Basic Settings" +msgstr "基础设置" + +msgid "Set the basic settings of Webd" +msgstr "配置 Webd 的基础设置" + +msgid "Listening Port" +msgstr "监听端口" + +msgid "Webd - A lightweight self hosted netdisk" +msgstr "Webd 是一款轻量级的 (self-hosted) 自建网盘软件, 界面简洁易用, 速度快资源占用低" + +msgid "Listen IPv6" +msgstr "监听 IPv6" + +msgid "Listen both IPv4 and IPv6 Address" +msgstr "同时监听 IPv4 和 IPv6 地址" + +msgid "Enable Anonymous Access" +msgstr "允许匿名访问" + +msgid "Anonymous Permission" +msgstr "匿名访问权限" + +msgid "Anonymous access is allowed when enabled (Not Safe)" +msgstr "启用后将允许匿名用户访问 (不安全)" + +msgid "At least one permission must be choosed to allow anonymous access" +msgstr "若要允许匿名访问则勾选至少一个权限" + +msgid "Binary Path" +msgstr "程序路径" + +msgid "Webd binary Path" +msgstr "Webd 程序路径" + +msgid "Config Path" +msgstr "配置文件路径" + +msgid "Webd config Path" +msgstr "Webd 配置文件路径" + +msgid "Local Directory" +msgstr "本地路径" + +msgid "Directory of Webd" +msgstr "Webd 监听路径" + +msgid "Recycle Bin" +msgstr "回收站" + +msgid "Automatically create recycle bin directory" +msgstr "允许自动创建回收站目录" + +msgid "Set the username, password and permissions. Maximum for 2 accounts" +msgstr "设置用户名和密码, 以及单个用户的权限, 最多支持设置两个账号" + +msgid "Read files" +msgstr "读取文件" + +msgid "Obtain file list" +msgstr "获取文件列表" + +msgid "Upload files" +msgstr "上传文件" + +msgid "Remove files" +msgstr "删除或移动文件" + +msgid "Show hidden files" +msgstr "显示隐藏文件" + +msgid "Play media" +msgstr "播放媒体" + +msgid "Append download attribute" +msgstr "附加下载属性" diff --git a/luci-app-webd/po/zh_Hans/webd.po b/luci-app-webd/po/zh_Hans/webd.po new file mode 100755 index 000000000..77f471b5f --- /dev/null +++ b/luci-app-webd/po/zh_Hans/webd.po @@ -0,0 +1,83 @@ +msgid "Webd Netdisk" +msgstr "Webd 网盘" + +msgid "Official Website" +msgstr "官网" + +msgid "Basic Settings" +msgstr "基础设置" + +msgid "Set the basic settings of Webd" +msgstr "配置 Webd 的基础设置" + +msgid "Listening Port" +msgstr "监听端口" + +msgid "Webd - A lightweight self hosted netdisk" +msgstr "Webd 是一款轻量级的 (self-hosted) 自建网盘软件, 界面简洁易用, 速度快资源占用低" + +msgid "Listen IPv6" +msgstr "监听 IPv6" + +msgid "Listen both IPv4 and IPv6 Address" +msgstr "同时监听 IPv4 和 IPv6 地址" + +msgid "Enable Anonymous Access" +msgstr "允许匿名访问" + +msgid "Anonymous Permission" +msgstr "匿名访问权限" + +msgid "Anonymous access is allowed when enabled (Not Safe)" +msgstr "启用后将允许匿名用户访问 (不安全)" + +msgid "At least one permission must be choosed to allow anonymous access" +msgstr "若要允许匿名访问则勾选至少一个权限" + +msgid "Binary Path" +msgstr "程序路径" + +msgid "Webd binary Path" +msgstr "Webd 程序路径" + +msgid "Config Path" +msgstr "配置文件路径" + +msgid "Webd config Path" +msgstr "Webd 配置文件路径" + +msgid "Local Directory" +msgstr "本地路径" + +msgid "Directory of Webd" +msgstr "Webd 监听路径" + +msgid "Recycle Bin" +msgstr "回收站" + +msgid "Automatically create recycle bin directory" +msgstr "允许自动创建回收站目录" + +msgid "Set the username, password and permissions. Maximum for 2 accounts" +msgstr "设置用户名和密码, 以及单个用户的权限, 最多支持设置两个账号" + +msgid "Read files" +msgstr "读取文件" + +msgid "Obtain file list" +msgstr "获取文件列表" + +msgid "Upload files" +msgstr "上传文件" + +msgid "Remove files" +msgstr "删除或移动文件" + +msgid "Show hidden files" +msgstr "显示隐藏文件" + +msgid "Play media" +msgstr "播放媒体" + +msgid "Append download attribute" +msgstr "附加下载属性" diff --git a/luci-app-webd/root/etc/config/webd b/luci-app-webd/root/etc/config/webd new file mode 100755 index 000000000..acd693997 --- /dev/null +++ b/luci-app-webd/root/etc/config/webd @@ -0,0 +1,13 @@ + +config webd + option enable '0' + option webd_port '9212' + +config users + option enable_read '1' + option enable_read_list '1' + option enable_upload '1' + option enable_move '1' + option enable_showhide '0' + option username 'root' + option password 'password' diff --git a/luci-app-webd/root/etc/init.d/webd b/luci-app-webd/root/etc/init.d/webd new file mode 100755 index 000000000..84c678bf4 --- /dev/null +++ b/luci-app-webd/root/etc/init.d/webd @@ -0,0 +1,119 @@ +#!/bin/sh /etc/rc.common + +START=99 +USE_PROCD=1 +LOGGER="logger -t [Webd]" + +start_service() { + local basic_list="enable webd_conf webd_bin webd_port webd_root enable_recyclebin enable_anonymous anonymous_perm enable_ipv6" + local users_list="enable_read enable_read_list enable_upload enable_move enable_showhide" + for i in $(echo $basic_list);do + local eval $i="$(uci_get_by_type webd 0 $i)" + done;unset i + if [ "$enable" == 1 ] + then + [ ! -r "$webd_root" -o ! -d "$webd_root" ] && EXIT "Unable to access $webd_root,exit ..." + [ ! -x "$webd_bin" ] && EXIT "Unable to access $webd_bin,exit ..." + if [ "$enable_recyclebin" == 1 -a ! -d "$webd_root/.Trash" ] + then + ${LOGGER} "Creating Recycle Bin directory ..." + mkdir -p $webd_root/.Trash || EXIT "Failed to create Recycle Bin directory,exit ..." + fi + ${LOGGER} "Removing old config file ..." + rm -f $webd_conf + touch -a $webd_conf || EXIT "Failed to create config,exit ..." + [ "$enable_ipv6" == 1 ] && webd_port="[::]:${webd_port}" + if [ "$enable_anonymous" != 0 ] + then + if [ -n "$anonymous_perm" ] + then + + unset enable_anonymous + for i in $(echo $anonymous_perm);do + enable_anonymous="$enable_anonymous$i" + done + unset i + else + enable_anonymous=0 + uci set webd.@webd[0].enable_anonymous=0 + uci commit webd + fi + fi + echo "Webd.Listen $webd_port" >> $webd_conf + echo "Webd.Root $webd_root" >> $webd_conf + echo "Webd.Guest $enable_anonymous" >> $webd_conf + + for u in 0 1;do + for i in $(echo $users_list);do + eval ${i}=$(uci_get_by_type users $u $i 0) + echo "$users_list" | grep -q $i + [ "$?" == 0 ] && eval perm_bin=$(eval echo '$'perm_bin)$(uci_get_by_type users $u $i) + done + unset i + username=$(uci_get_by_type users $u username) + password=$(uci_get_by_type users $u password) + if [ -n "$username" ] + then + eval perm=$(perm_converter $(eval echo '$'perm_bin) | tail -n 1) + if [ -n "$(eval echo '$'perm)" ] + then + ${LOGGER} "Creating account for User $username ..." + echo "Webd.User $(eval echo '$'perm) $username $password" >> $webd_conf + else + ${LOGGER} "Removing excessive user config ..." + uci delete webd.@users[$u] + uci commit webd + fi + unset perm_bin + fi + done + unset u + ps -efww | grep "$webd_bin" | awk '{print $1}' | xargs kill -9 2> /dev/null + ${LOGGER} "Starting Webd Service ..." + procd_open_instance + procd_set_param command $webd_bin -c $webd_conf + procd_set_param respawn + procd_close_instance + else + stop_service + ${LOGGER} "Webd Service is now disabled ..." + fi +} + +stop_service() { + ${LOGGER} "Stopping Webd Service ..." +} + +service_triggers() { + procd_add_reload_trigger "webd" +} + +uci_get_by_type() { + local ret=$(uci get webd.@$1[$2].$3 2>/dev/null) + echo ${ret:=$4} +} + +EXIT() { + ${LOGGER} $* + exit +} + +perm_converter() { + local u i=1 + echo $1 | egrep -o [0-1] | while read X + do + if [ "$X" == 1 ] + then + case $i in + 1)u=r;; + 2)u=l;; + 3)u=u;; + 4)u=m;; + 5)u=S;; + esac + [ -n "$u" ] && a="$a$u" + echo "$a" + fi + i=$(($i + 1)) + done +} diff --git a/luci-app-webd/root/etc/uci-defaults/luci-webd b/luci-app-webd/root/etc/uci-defaults/luci-webd new file mode 100755 index 000000000..41934849f --- /dev/null +++ b/luci-app-webd/root/etc/uci-defaults/luci-webd @@ -0,0 +1,11 @@ +#!/bin/sh + +uci -q batch <<-EOF >/dev/null + delete ucitrack.@webd[-1] + add ucitrack webd + set ucitrack.@webd[-1].init=webd + commit ucitrack +EOF + +rm -f /tmp/luci-indexcache +exit 0 diff --git a/luci-app-webd/root/usr/share/rpcd/acl.d/luci-app-webd.json b/luci-app-webd/root/usr/share/rpcd/acl.d/luci-app-webd.json new file mode 100644 index 000000000..8247638a0 --- /dev/null +++ b/luci-app-webd/root/usr/share/rpcd/acl.d/luci-app-webd.json @@ -0,0 +1,11 @@ +{ + "luci-app-webd": { + "description": "Grant UCI access for luci-app-webd", + "read": { + "uci": [ "webd" ] + }, + "write": { + "uci": [ "webd" ] + } + } +} diff --git a/lucky/Makefile b/lucky/Makefile new file mode 100644 index 000000000..2cb9b7bd5 --- /dev/null +++ b/lucky/Makefile @@ -0,0 +1,77 @@ +# SPDX-License-Identifier: GPL-3.0-only +# +# Copyright (C) 2021-2022 sirpdboy +# +# This is free software, licensed under the Apache License, Version 2.0 . +# + +include $(TOPDIR)/rules.mk + +PKG_NAME:=lucky +PKG_VERSION:=2.24.0 +PKG_RELEASE:=1 +PKGARCH:=all + +ifeq ($(ARCH),mipsel) + LUCKY_ARCH:=mipsle_softfloat +endif +ifeq ($(ARCH),mips) + LUCKY_ARCH:=mips_softfloat +endif +ifeq ($(ARCH),i386) + LUCKY_ARCH:=i386 +endif +ifeq ($(ARCH),x86_64) + LUCKY_ARCH:=x86_64 +endif +ifeq ($(ARCH),arm) + LUCKY_ARCH:=armv7 +endif +ifeq ($(BOARD),bcm53xx) + LUCKY_ARCH:=armv6 +ifeq ($(word 2,$(subst +,$(space),$(call qstrip,$(CONFIG_CPU_TYPE)))),) + LUCKY_ARCH:=armv5 +endif +endif +ifeq ($(BOARD),kirkwood) + LUCKY_ARCH:=armv5 +endif +ifeq ($(ARCH),aarch64) + LUCKY_ARCH:=arm64 +endif + +PKG_LICENSE:=GPL-3.0-only +PKG_LICENSE_FILES:=LICENSE +PKG_MAINTAINER:=GDY666 + +PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION) +PKG_HASH:=d7409d3ab8742e2b7fa39339fe3b10edb969e01e177ea5c6f2c8b1776ce2593c + +include $(INCLUDE_DIR)/package.mk + +define Package/$(PKG_NAME) + SECTION:=net + CATEGORY:=Network + TITLE:=Lucky dynamic domain name ddns-go service, socat,frp + DEPENDS:=@(i386||x86_64||arm||aarch64||mipsel||mips) + URL:=https://github.com/gdy666/lucky +endef + +define Package/$(PKG_NAME)/description + Main functions of Lucky: dynamic domain name ddns-go service, socat,reverse proxy ,wake on lan +endef + +define Build/Prepare + [ ! -f $(PKG_BUILD_DIR)/$(PKG_NAME)_$(PKG_VERSION)_Linux_$(LUCKY_ARCH).tar.gz ] && wget https://github.com/gdy666/lucky/releases/download/v$(PKG_VERSION)/$(PKG_NAME)_$(PKG_VERSION)_Linux_$(LUCKY_ARCH).tar.gz -O $(PKG_BUILD_DIR)/$(PKG_NAME)_$(PKG_VERSION)_Linux_$(LUCKY_ARCH).tar.gz + tar -xzvf $(PKG_BUILD_DIR)/$(PKG_NAME)_$(PKG_VERSION)_Linux_$(LUCKY_ARCH).tar.gz -C $(PKG_BUILD_DIR) +endef + +define Build/Compile +endef + +define Package/$(PKG_NAME)/install + $(INSTALL_DIR) $(1)/usr/bin + $(INSTALL_BIN) $(PKG_BUILD_DIR)/lucky $(1)/usr/bin/lucky +endef + +$(eval $(call BuildPackage,$(PKG_NAME))) diff --git a/microsocks/Makefile b/microsocks/Makefile new file mode 100644 index 000000000..5d8072915 --- /dev/null +++ b/microsocks/Makefile @@ -0,0 +1,43 @@ +# SPDX-License-Identifier: GPL-3.0-only +# +# Copyright (C) 2021 ImmortalWrt.org + +include $(TOPDIR)/rules.mk + +PKG_NAME:=microsocks +PKG_VERSION:=1.0.5 +PKG_RELEASE:=1 + +PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz +PKG_SOURCE_URL:=https://codeload.github.com/rofl0r/microsocks/tar.gz/v$(PKG_VERSION)? +PKG_HASH:=939d1851a18a4c03f3cc5c92ff7a50eaf045da7814764b4cb9e26921db15abc8 + +PKG_LICENSE:=MIT +PKG_LICENSE_FILES:=COPYING +PKG_MAINTAINER:=lean + +PKG_BUILD_PARALLEL:=1 +PKG_INSTALL:=1 + +include $(INCLUDE_DIR)/package.mk + +define Package/microsocks + SECTION:=net + CATEGORY:=Network + SUBMENU:=Web Servers/Proxies + TITLE:=Tiny, portable SOCKS5 server + URL:=https://github.com/rofl0r/microsocks + DEPENDS:=+libpthread +endef + +define Package/microsocks/description + A SOCKS5 service that you can run on your remote boxes to tunnel connections + through them, if for some reason SSH doesn't cut it for you. +endef + +define Package/microsocks/install + $(INSTALL_DIR) $(1)/usr/bin + $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/local/bin/microsocks $(1)/usr/bin/microsocks +endef + +$(eval $(call BuildPackage,microsocks)) diff --git a/mosdns/Makefile b/mosdns/Makefile new file mode 100644 index 000000000..62d32d199 --- /dev/null +++ b/mosdns/Makefile @@ -0,0 +1,44 @@ +# SPDX-License-Identifier: GPL-3.0-only +# +# Copyright (C) 2021 ImmortalWrt.org + +include $(TOPDIR)/rules.mk + +PKG_NAME:=mosdns +PKG_VERSION:=5.3.3 +PKG_RELEASE:=1 + +PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz +PKG_SOURCE_URL:=https://codeload.github.com/IrineSistiana/mosdns/tar.gz/v$(PKG_VERSION)? +PKG_HASH:=1d7eeaa735cb48ed2d436797d7f2a82541699f74647cd293ee411a72cdc65f5f + +PKG_LICENSE:=GPL-3.0 +PKG_LICENSE_FILE:=LICENSE +PKG_MAINTAINER:=Tianling Shen + +PKG_BUILD_DEPENDS:=golang/host +PKG_BUILD_PARALLEL:=1 +PKG_USE_MIPS16:=0 +PKG_BUILD_FLAGS:=no-mips16 + +GO_PKG:=github.com/IrineSistiana/mosdns +GO_PKG_LDFLAGS_X:=main.version=v$(PKG_VERSION) + +include $(INCLUDE_DIR)/package.mk +include $(TOPDIR)/feeds/packages/lang/golang/golang-package.mk + +define Package/mosdns + SECTION:=net + CATEGORY:=Network + SUBMENU:=IP Addresses and Names + TITLE:=A plug-in DNS forwarder/splitter + URL:=https://github.com/IrineSistiana/mosdns + DEPENDS:=$(GO_ARCH_DEPENDS) +ca-bundle +endef + +define Package/mosdns/install + $(call GoPackage/Package/Install/Bin,$(1)) +endef + +$(eval $(call GoBinPackage,mosdns)) +$(eval $(call BuildPackage,mosdns)) diff --git a/mosdns/patches/203-add-response-for-bad-request-in-ServeHTTP-handler.patch b/mosdns/patches/203-add-response-for-bad-request-in-ServeHTTP-handler.patch new file mode 100644 index 000000000..38b7974bf --- /dev/null +++ b/mosdns/patches/203-add-response-for-bad-request-in-ServeHTTP-handler.patch @@ -0,0 +1,19 @@ +From 0b86b89629f32e7c8b859239aa1a4814f256053c Mon Sep 17 00:00:00 2001 +From: sbwml +Date: Thu, 28 Sep 2023 16:42:54 +0800 +Subject: [PATCH 3/5] add response for bad request in ServeHTTP handler + +--- + pkg/server/http_handler.go | 1 + + 1 file changed, 1 insertion(+) + +--- a/pkg/server/http_handler.go ++++ b/pkg/server/http_handler.go +@@ -93,6 +93,7 @@ func (h *HttpHandler) ServeHTTP(w http.R + if err != nil { + h.warnErr(req, "invalid request", err) + w.WriteHeader(http.StatusBadRequest) ++ w.Write([]byte("Bad Request")) + return + } + diff --git a/mosdns/patches/204-black_hole-apply-Fisher-Yates-shuffle-algorithm-to-r.patch b/mosdns/patches/204-black_hole-apply-Fisher-Yates-shuffle-algorithm-to-r.patch new file mode 100644 index 000000000..fa18e86c1 --- /dev/null +++ b/mosdns/patches/204-black_hole-apply-Fisher-Yates-shuffle-algorithm-to-r.patch @@ -0,0 +1,51 @@ +From e34dca717e78d24a84b98c2b5d371c4253b7e260 Mon Sep 17 00:00:00 2001 +From: sbwml +Date: Wed, 20 Sep 2023 14:51:19 +0800 +Subject: [PATCH 4/5] black_hole: apply Fisher-Yates shuffle algorithm to + randomize IP order + +--- + plugin/executable/black_hole/black_hole.go | 15 +++++++++++++++ + 1 file changed, 15 insertions(+) + +--- a/plugin/executable/black_hole/black_hole.go ++++ b/plugin/executable/black_hole/black_hole.go +@@ -27,6 +27,8 @@ import ( + "github.com/miekg/dns" + "net/netip" + "strings" ++ "math/rand" ++ "sync" + ) + + const PluginType = "black_hole" +@@ -40,6 +42,7 @@ var _ sequence.Executable = (*BlackHole) + type BlackHole struct { + ipv4 []netip.Addr + ipv6 []netip.Addr ++ shuffleMutex sync.Mutex + } + + // QuickSetup format: [ipv4|ipv6] ... +@@ -65,9 +68,21 @@ func NewBlackHole(ips []string) (*BlackH + return b, nil + } + ++func (b *BlackHole) shuffleIPs() { ++ b.shuffleMutex.Lock() ++ defer b.shuffleMutex.Unlock() ++ rand.Shuffle(len(b.ipv4), func(i, j int) { ++ b.ipv4[i], b.ipv4[j] = b.ipv4[j], b.ipv4[i] ++ }) ++ rand.Shuffle(len(b.ipv6), func(i, j int) { ++ b.ipv6[i], b.ipv6[j] = b.ipv6[j], b.ipv6[i] ++ }) ++} ++ + // Exec implements sequence.Executable. It set a response with given ips if + // query has corresponding qtypes. + func (b *BlackHole) Exec(_ context.Context, qCtx *query_context.Context) error { ++ b.shuffleIPs() + if r := b.Response(qCtx.Q()); r != nil { + qCtx.SetResponse(r) + } diff --git a/mosdns/patches/205-format-logtime.patch b/mosdns/patches/205-format-logtime.patch new file mode 100644 index 000000000..1628ad041 --- /dev/null +++ b/mosdns/patches/205-format-logtime.patch @@ -0,0 +1,46 @@ +From 2dc08749e2de8f19ef869e7f89c9979edbbc71ff Mon Sep 17 00:00:00 2001 +From: sbwml +Date: Wed, 20 Sep 2023 21:05:18 +0800 +Subject: [PATCH 5/5] format logtime + +--- + mlog/logger.go | 18 ++++++++++++++---- + 1 file changed, 14 insertions(+), 4 deletions(-) + +--- a/mlog/logger.go ++++ b/mlog/logger.go +@@ -21,9 +21,11 @@ package mlog + + import ( + "fmt" ++ "os" ++ "time" ++ + "go.uber.org/zap" + "go.uber.org/zap/zapcore" +- "os" + ) + + type LogConfig struct { +@@ -64,10 +66,18 @@ func NewLogger(lc LogConfig) (*zap.Logge + out = stderr + } + +- if lc.Production { +- return zap.New(zapcore.NewCore(zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig()), out, lvl)), nil ++ encoderConfig := zap.NewDevelopmentEncoderConfig() ++ encoderConfig.EncodeTime = func(t time.Time, enc zapcore.PrimitiveArrayEncoder) { ++ enc.AppendString(t.Format("2006-01-02 15:04:05")) + } +- return zap.New(zapcore.NewCore(zapcore.NewConsoleEncoder(zap.NewDevelopmentEncoderConfig()), out, lvl)), nil ++ ++ core := zapcore.NewCore( ++ zapcore.NewConsoleEncoder(encoderConfig), ++ out, ++ lvl, ++ ) ++ ++ return zap.New(core), nil + } + + // L is a global logger. diff --git a/naiveproxy/Makefile b/naiveproxy/Makefile new file mode 100644 index 000000000..8d6e2e245 --- /dev/null +++ b/naiveproxy/Makefile @@ -0,0 +1,108 @@ +# SPDX-License-Identifier: GPL-3.0-only +# +# Copyright (C) 2021 ImmortalWrt.org + +include $(TOPDIR)/rules.mk + +PKG_NAME:=naiveproxy +PKG_VERSION:=143.0.7499.109-2 +PKG_RELEASE:=1 + +# intel 80386 & riscv64 & cortex-a76 +ifeq ($(ARCH_PREBUILT),aarch64_cortex-a76) + ARCH_PREBUILT:=aarch64_generic +else ifeq ($(ARCH_PREBUILT),i386_pentium-mmx) + ARCH_PREBUILT:=x86 +else ifeq ($(ARCH_PREBUILT),i386_pentium4) + ARCH_PREBUILT:=x86 +else ifeq ($(ARCH_PREBUILT),riscv64_riscv64) + ARCH_PREBUILT:=riscv64 +endif + +ifeq ($(ARCH_PACKAGES),aarch64_cortex-a53) + PKG_HASH:=9bda77ac7c551e1fd33b2db21cc235e4d2e35c414655018f53e0d419ac2b46d1 +else ifeq ($(ARCH_PACKAGES),aarch64_cortex-a72) + PKG_HASH:=5a8a02055d14abd56916fb1e2ede9b56b55785fc842c893ac9cfdc98f1ceb6bd +else ifeq ($(ARCH_PACKAGES),aarch64_generic) + PKG_HASH:=d2b41880a40e204ebe91baaf8202d0d1f3004b5335266afa426c680f2f0a909e +else ifeq ($(ARCH_PACKAGES),arm_arm1176jzf-s_vfp) + PKG_HASH:=76df8b546ed640ed2e581f32ab837fc487aae0a80a5985a42405c2c8bad87c18 +else ifeq ($(ARCH_PACKAGES),arm_arm926ej-s) + PKG_HASH:=27b28beaa032165e9b93d423353ced3cfe594c71353fd9d109699f330b785732 +else ifeq ($(ARCH_PACKAGES),arm_cortex-a15_neon-vfpv4) + PKG_HASH:=5e2dd76559c1eef027471ddadb646a5aaff7f4d2b337bf9aa609bc38dde4247b +else ifeq ($(ARCH_PACKAGES),arm_cortex-a5_vfpv4) + PKG_HASH:=49a9c7eecab54155a31c3bfae71cf5193a881189715c7b4d29ce50a8c5c759d6 +else ifeq ($(ARCH_PACKAGES),arm_cortex-a7) + PKG_HASH:=99b22874957bce541a0f8cbc0d23c501b85c20e8645bfa777a9f358d0b121d03 +else ifeq ($(ARCH_PACKAGES),arm_cortex-a7_neon-vfpv4) + PKG_HASH:=c0ff70018cf45b8115c9dd3f56ffc7d82b9c381d97ea75722c56401689d2948c +else ifeq ($(ARCH_PACKAGES),arm_cortex-a7_vfpv4) + PKG_HASH:=725add2f90194dc44397e1034d861c1f88387eeea2f32baaa07bc384bfab2176 +else ifeq ($(ARCH_PACKAGES),arm_cortex-a8_vfpv3) + PKG_HASH:=3f281c94e72d47b3676cd9135e5b83858421c2a108e7b121629480f0fd5b4830 +else ifeq ($(ARCH_PACKAGES),arm_cortex-a9) + PKG_HASH:=b9623d595a35609050c9638c8d2903cdb51eb56675ff4109627d66d8a6a993e3 +else ifeq ($(ARCH_PACKAGES),arm_cortex-a9_neon) + PKG_HASH:=ab5a699ef830d65344f014547ae6ce73e0f25103ad02d3e80a5d441e19c7e21e +else ifeq ($(ARCH_PACKAGES),arm_cortex-a9_vfpv3-d16) + PKG_HASH:=294b676fb05f7042e16ca5871200915f3bcc73a6650c99f17857d651af6bb920 +else ifeq ($(ARCH_PACKAGES),arm_mpcore) + PKG_HASH:=0498d19350ae3e6c064913758199c53014254c4765a106eac1913fd7642eff41 +else ifeq ($(ARCH_PACKAGES),arm_xscale) + PKG_HASH:=40175f13102597b88f92c664bf5c595ab69b248a5b64229a6aba388be77345b4 +else ifeq ($(ARCH_PACKAGES),mipsel_24kc) + PKG_HASH:=63b1f692d2643de026d1a53699a65e7f30ee5c226beaa6cc255189a7c04d5817 +else ifeq ($(ARCH_PACKAGES),mipsel_mips32) + PKG_HASH:=e5dd26061343287e6636003795763bcc9d8d83f4b7096246bdc81ed5da45253c +else ifeq ($(ARCH_PACKAGES),riscv64) + PKG_HASH:=38c6a3fafbf073a83229f6b0629f725006e928f06d9d2353bdd3c072ef9a99bf +else ifeq ($(ARCH_PACKAGES),x86) + PKG_HASH:=a3e8aad951330b995273176cc310038971f4f3fff56ab047d5e60e3a0eb67d14 +else ifeq ($(ARCH_PACKAGES),x86_64) + PKG_HASH:=7387ce2af58463735d839f45d8564626cdc1baea3e053c2479ba5fe0f2fa721f +else + PKG_HASH:=dummy +endif + +PKG_SOURCE:=naiveproxy-v$(PKG_VERSION)-openwrt-$(ARCH_PACKAGES).tar.xz +PKG_SOURCE_URL:=https://github.com/klzgrad/naiveproxy/releases/download/v$(PKG_VERSION)/ +PKG_BUILD_DIR:=$(BUILD_DIR)/naiveproxy-v$(PKG_VERSION)-openwrt-$(ARCH_PACKAGES) + +PKG_LICENSE:=BSD 3-Clause +PKG_LICENSE_FILES:=LICENSE +PKG_MAINTAINER:=sbwml + +include $(INCLUDE_DIR)/package.mk + +define Package/naiveproxy + SECTION:=net + CATEGORY:=Network + SUBMENU:=Web Servers/Proxies + TITLE:=Make a fortune quietly + URL:=https://github.com/klzgrad/naiveproxy + DEPENDS:=@(aarch64||arm||i386||mipsel||riscv64||x86_64) +libatomic +endef + +define Package/naiveproxy/description + NaïveProxy uses Chrome's network stack to camouflage traffic with strong + censorship resistance and low detectability. Reusing Chrome's stack also + ensures best practices in performance and security. +endef + +define Package/naiveproxy/conffiles +/etc/config/naiveproxy +endef + +define Build/Compile +endef + +define Package/naiveproxy/install + $(INSTALL_DIR) $(1)/usr/bin + $(INSTALL_BIN) $(PKG_BUILD_DIR)/naive $(1)/usr/bin/naive + $(INSTALL_DIR) $(1)/etc/config $(1)/etc/init.d + $(INSTALL_CONF) $(CURDIR)/files/naiveproxy.config $(1)/etc/config/naiveproxy + $(INSTALL_BIN) $(CURDIR)/files/naiveproxy.init $(1)/etc/init.d/naiveproxy +endef + +$(eval $(call BuildPackage,naiveproxy)) diff --git a/naiveproxy/files/naiveproxy.config b/naiveproxy/files/naiveproxy.config new file mode 100644 index 000000000..439e30bef --- /dev/null +++ b/naiveproxy/files/naiveproxy.config @@ -0,0 +1,6 @@ + +config naiveproxy 'config' + option enable '0' + option listen_addr '' + option server_addr '' + option extra_argument '' diff --git a/naiveproxy/files/naiveproxy.init b/naiveproxy/files/naiveproxy.init new file mode 100644 index 000000000..e483b48a3 --- /dev/null +++ b/naiveproxy/files/naiveproxy.init @@ -0,0 +1,47 @@ +#!/bin/sh /etc/rc.common +# Copyright (C) 2021 ImmortalWrt + +. /lib/functions.sh +. /lib/functions/procd.sh + +USE_PROCD=1 + +START=99 +STOP=10 + +init_conf() { + config_load "naiveproxy" + config_get "enable" "config" "enable" "0" + config_get "listen_addr" "config" "listen_addr" + config_get "server_addr" "config" "server_addr" + config_get "extra_argument" "config" "extra_argument" +} + +start_service() { + init_conf + [ "${enable}" == "1" ] || return 0 + + procd_open_instance naiveproxy + + procd_set_param command naive + procd_append_param command --listen="${listen_addr}" + procd_append_param command --proxy="${server_addr}" + [ -n "${extra_argument}" ] && procd_append_param command "${extra_argument}" + + procd_set_param respawn ${respawn_threshold:-3600} ${respawn_timeout:-5} ${respawn_retry:-5} + procd_set_param limits core="unlimited" + procd_set_param stdout 1 + procd_set_param stderr 1 + + procd_close_instance +} + +reload_service() +{ + stop + start +} + +service_triggers() { + procd_add_reload_trigger "naiveproxy" +} diff --git a/natter/Makefile b/natter/Makefile new file mode 100755 index 000000000..4d2ab4e3a --- /dev/null +++ b/natter/Makefile @@ -0,0 +1,40 @@ +# Copyright (C) 2020-2022 Hyy2001X + +include $(TOPDIR)/rules.mk + +PKG_NAME:=natter +PKG_VERSION=0.9 +PKG_RELEASE:=$(AUTORELEASE) +PKG_SOURCE_VERSION:=42005887f95dcfdfd5ed995bf237003f2f80ccfd + +PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(PKG_SOURCE_VERSION) +PKG_SOURCE:=$(PKG_NAME)-$(PKG_SOURCE_VERSION).tar.gz +PKG_SOURCE_URL:=https://codeload.github.com/MikeWang000000/Natter/tar.gz/$(PKG_SOURCE_VERSION)? +PKG_HASH:=skip + +include $(INCLUDE_DIR)/package.mk + +define Package/$(PKG_NAME) + SECTION:=net + CATEGORY:=Network + TITLE:=Open Port under FullCone NAT (NAT 1) + URL:=https://github.com/MikeWang000000/Natter + DEPENDS:=+python3-light +iptables-mod-fullconenat +endef + +define Build/Prepare + mkdir -p $(PKG_BUILD_DIR) + tar -zxvf $(DL_DIR)/$(PKG_SOURCE) -C $(PKG_BUILD_DIR) +endef + +define Build/Compile +endef + +define Package/$(PKG_NAME)/install + $(INSTALL_DIR) $(1)/usr/share/$(PKG_NAME) + $(INSTALL_BIN) $(PKG_BUILD_DIR)/Natter-$(PKG_SOURCE_VERSION)/natter.py $(1)/usr/share/$(PKG_NAME)/natter.py + $(INSTALL_BIN) $(PKG_BUILD_DIR)/Natter-$(PKG_SOURCE_VERSION)/natter-config.template.json $(1)/usr/share/$(PKG_NAME)/natter-config.template.json + $(INSTALL_BIN) $(PKG_BUILD_DIR)/Natter-$(PKG_SOURCE_VERSION)/natter-hook.sh $(1)/usr/share/$(PKG_NAME)/natter-hook.sh +endef + +$(eval $(call BuildPackage,$(PKG_NAME))) diff --git a/natter2/Makefile b/natter2/Makefile new file mode 100755 index 000000000..02783ef73 --- /dev/null +++ b/natter2/Makefile @@ -0,0 +1,40 @@ +# Copyright (C) 2020-2024 Hyy2001X + +include $(TOPDIR)/rules.mk + +PKG_NAME:=natter2 +PKG_VERSION=2.0.0-rc3 +PKG_RELEASE:=$(AUTORELEASE) +PKG_SOURCE_VERSION:=3a11cf0164794570d677d3ef8d31e68b984d8a6b + +PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(PKG_SOURCE_VERSION) +PKG_SOURCE:=$(PKG_NAME)-$(PKG_SOURCE_VERSION).tar.gz +PKG_SOURCE_URL:=https://codeload.github.com/MikeWang000000/Natter/tar.gz/$(PKG_SOURCE_VERSION)? +PKG_HASH:=skip + +include $(INCLUDE_DIR)/package.mk + +define Package/$(PKG_NAME) + SECTION:=net + CATEGORY:=Network + TITLE:=Open Port under FullCone NAT (NAT 1) + URL:=https://github.com/MikeWang000000/Natter + DEPENDS:=+python3-light +endef + +define Build/Prepare + mkdir -p $(PKG_BUILD_DIR) + tar -zxvf $(DL_DIR)/$(PKG_SOURCE) -C $(PKG_BUILD_DIR) +endef + +define Build/Compile +endef + +define Package/$(PKG_NAME)/install + $(INSTALL_DIR) $(1)/usr/share/$(PKG_NAME) + $(INSTALL_BIN) $(PKG_BUILD_DIR)/Natter-$(PKG_SOURCE_VERSION)/natter.py $(1)/usr/share/$(PKG_NAME)/natter.py + $(INSTALL_DIR) $(1)/usr/share/$(PKG_NAME)/natter-check + $(INSTALL_BIN) $(PKG_BUILD_DIR)/Natter-$(PKG_SOURCE_VERSION)/natter-check/natter-check.py $(1)/usr/share/$(PKG_NAME)/natter-check/natter-check.py +endef + +$(eval $(call BuildPackage,$(PKG_NAME))) diff --git a/pdnsd-alt/Makefile b/pdnsd-alt/Makefile new file mode 100644 index 000000000..f40096c52 --- /dev/null +++ b/pdnsd-alt/Makefile @@ -0,0 +1,59 @@ +# SPDX-License-Identifier: GPL-3.0-only +# +# Copyright (C) 2021 ImmortalWrt.org + +include $(TOPDIR)/rules.mk + +PKG_NAME:=pdnsd +PKG_VERSION:=1.2.9b-par +PKG_RELEASE:=3 + +PKG_SOURCE_PROTO:=git +PKG_SOURCE_URL:=https://github.com/shadowsocks/pdnsd.git +PKG_SOURCE_DATE:=2012-04-26 +PKG_SOURCE_VERSION:=a8e46ccba7b0fa2230d6c42ab6dcd92926f6c21d +PKG_MIRROR_HASH:=387f991f9c4fe33dedb6028626ecf8aed4b6c5cacaa6f07bf48668e4cfdd8e14 + +PKG_BUILD_PARALLEL:=1 +PKG_INSTALL:=1 + +include $(INCLUDE_DIR)/package.mk + +define Package/pdnsd-alt + SECTION:=net + CATEGORY:=Network + SUBMENU:=IP Addresses and Names + TITLE:=Proxy DNS Server + DEPENDS:=+libpthread +endef + +define Package/pdnsd-alt/description + pdnsd, is an IPv6 capable proxy DNS server with permanent caching (the cache + contents are written to hard disk on exit) that is designed to cope with + unreachable or down DNS servers (for example in dial-in networking). + + pdnsd can be used with applications that do dns lookups, eg on startup, and + can't be configured to change that behaviour, to prevent the often + minute-long hangs (or even crashes) that result from stalled dns queries. +endef + +TARGET_CFLAGS += -I$(STAGING_DIR)/usr/include + +CONFIGURE_ARGS += \ + --with-cachedir=/var/pdnsd \ + --with-target=Linux + +define Package/pdnsd-alt/install + $(INSTALL_DIR) $(1)/usr/sbin + $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/pdnsd $(1)/usr/sbin/pdnsd + $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/pdnsd-ctl $(1)/usr/sbin/pdnsd-ctl + + #$(INSTALL_DIR) $(1)/etc + #$(INSTALL_CONF) $(PKG_INSTALL_DIR)/etc/pdnsd.conf.sample $(1)/etc/pdnsd.conf + #$(INSTALL_DIR) $(1)/etc/init.d + #$(INSTALL_BIN) ./files/pdnsd.init $(1)/etc/init.d/pdnsd +endef + +$(eval $(call BuildPackage,pdnsd-alt)) + + diff --git a/pdnsd-alt/files/pdnsd.init b/pdnsd-alt/files/pdnsd.init new file mode 100644 index 000000000..6db379923 --- /dev/null +++ b/pdnsd-alt/files/pdnsd.init @@ -0,0 +1,46 @@ +#!/bin/sh /etc/rc.common + +START=65 +NAME=pdnsd +DESC="proxy DNS server" + +DAEMON=/usr/sbin/pdnsd +PID_FILE=/var/run/$NAME.pid +CACHEDIR=/var/pdnsd +CACHE=$CACHEDIR/pdnsd.cache + +USER=nobody +GROUP=nogroup + +start() { + echo -n "Starting $DESC: $NAME" + + gen_cache + + $DAEMON --daemon -p $PID_FILE + echo " ." +} + +stop() { + echo -n "Stopping $DESC: $NAME" + kill `cat $PID_FILE` > /dev/null 2>&1 + rm -rf $PID_FILE + echo " ." +} + +restart() { + echo "Restarting $DESC: $NAME... " + stop + sleep 2 + start +} + +gen_cache() +{ + if ! test -f "$CACHE"; then + mkdir -p `dirname $CACHE` + dd if=/dev/zero of="$CACHE" bs=1 count=4 2> /dev/null + chown -R $USER.$GROUP $CACHEDIR + fi +} + diff --git a/pdnsd-alt/patches/010-no-doc-and-test.patch b/pdnsd-alt/patches/010-no-doc-and-test.patch new file mode 100644 index 000000000..1abf52e25 --- /dev/null +++ b/pdnsd-alt/patches/010-no-doc-and-test.patch @@ -0,0 +1,42 @@ +--- a/Makefile.am ++++ b/Makefile.am +@@ -1,5 +1,5 @@ + +-SUBDIRS = src doc contrib ++SUBDIRS = src contrib + + EXTRA_DIST = version ChangeLog.old COPYING.BSD README.par README.par.old PKGBUILD + +--- a/Makefile.in ++++ b/Makefile.in +@@ -196,7 +196,7 @@ threadlib = @threadlib@ + top_build_prefix = @top_build_prefix@ + top_builddir = @top_builddir@ + top_srcdir = @top_srcdir@ +-SUBDIRS = src doc contrib ++SUBDIRS = src contrib + EXTRA_DIST = version ChangeLog.old COPYING.BSD README.par README.par.old PKGBUILD + all: config.h + $(MAKE) $(AM_MAKEFLAGS) all-recursive +--- a/src/Makefile.am ++++ b/src/Makefile.am +@@ -15,7 +15,7 @@ EXTRA_DIST = make_rr_types_h.pl rr_types + + ## Try to do this last + +-SUBDIRS = . pdnsd-ctl rc test ++SUBDIRS = . pdnsd-ctl + + $(pdnsd_OBJECTS): rr_types.h + +--- a/src/Makefile.in ++++ b/src/Makefile.in +@@ -215,7 +215,7 @@ pdnsd_SOURCES = conf-parser.c conff.c co + freebsd_netinet_ip_icmp.h + + EXTRA_DIST = make_rr_types_h.pl rr_types.in +-SUBDIRS = . pdnsd-ctl rc test ++SUBDIRS = . pdnsd-ctl + all: all-recursive + + .SUFFIXES: diff --git a/pdnsd-alt/patches/020-headers.patch b/pdnsd-alt/patches/020-headers.patch new file mode 100644 index 000000000..16ecf459a --- /dev/null +++ b/pdnsd-alt/patches/020-headers.patch @@ -0,0 +1,66 @@ +--- a/src/conff.h ++++ b/src/conff.h +@@ -32,7 +32,7 @@ + #include + #include + #include +-#include ++#include + #include "ipvers.h" + #include "list.h" + +--- a/src/dns.h ++++ b/src/dns.h +@@ -27,7 +27,7 @@ + #include + #include + #include +-#include ++#include + #include + #include + #include "rr_types.h" +--- a/src/dns_answer.c ++++ b/src/dns_answer.c +@@ -37,7 +37,7 @@ + #include + #include + #ifdef HAVE_SYS_POLL_H +-#include ++#include + #endif + #include + #include +--- a/src/dns_query.c ++++ b/src/dns_query.c +@@ -23,7 +23,7 @@ + #include + #include + #ifdef HAVE_SYS_POLL_H +-#include ++#include + #endif + #include + #include +--- a/src/icmp.c ++++ b/src/icmp.c +@@ -28,7 +28,7 @@ + + #include + #ifdef HAVE_SYS_POLL_H +-#include ++#include + #endif + #include + #include +--- a/src/netdev.c ++++ b/src/netdev.c +@@ -59,7 +59,7 @@ + #include "ipvers.h" + #include + #include +-#include ++#include + #include + #include + #include diff --git a/rblibtorrent/Makefile b/rblibtorrent/Makefile index 8bf48d3f6..8b1813cc8 100644 --- a/rblibtorrent/Makefile +++ b/rblibtorrent/Makefile @@ -26,7 +26,10 @@ define Package/rblibtorrent CATEGORY:=Libraries TITLE:=C++ bittorrent library by Rasterbar Software URL:=https://www.libtorrent.org - DEPENDS:=+boost +boost-system +libatomic +libc +libgcc +libopenssl +libstdcpp +USE_GLIBC:libpthread + DEPENDS:= \ + +libatomic +libc +libgcc +libopenssl +libstdcpp +USE_GLIBC:libpthread \ + +boost +boost-program_options \ + $(if $(filter y,$(NEED_BOOST_SYSTEM)),,+boost-system) ABI_VERSION:=2.0 endef diff --git a/redsocks2/Makefile b/redsocks2/Makefile new file mode 100644 index 000000000..d35aae33f --- /dev/null +++ b/redsocks2/Makefile @@ -0,0 +1,48 @@ + +# SPDX-License-Identifier: GPL-3.0-only +# +# Copyright (C) 2021 ImmortalWrt.org + +include $(TOPDIR)/rules.mk + +PKG_NAME:=redsocks2 +PKG_VERSION:=0.71 +PKG_RELEASE:=1 + +PKG_SOURCE:=redsocks-$(PKG_VERSION).tar.gz +PKG_SOURCE_URL:=https://codeload.github.com/semigodking/redsocks/tar.gz/release-$(PKG_VERSION)? +PKG_HASH:=4ec470f47461ce5accffbefd56ead019ecd19b207ab8599461481c3d7e083bef +PKG_BUILD_DIR:=$(BUILD_DIR)/redsocks-release-$(PKG_VERSION) + +PKG_MAINTAINER:=semigodking +PKG_LICENSE:=Apache-2.0 +PKG_LICENSE_FILE:=LICENSE + +PKG_BUILD_PARALLEL:=1 + +include $(INCLUDE_DIR)/package.mk + +define Package/redsocks2 + SECTION:=net + CATEGORY:=Network + SUBMENU:=Web Servers/Proxies + TITLE:=Redirect TCP connection to proxy server + URL:=https://github.com/semigodking/redsocks + DEPENDS:=+libevent2 +libopenssl +endef + +define Package/redsocks2/description + This is a modified version of original redsocks. + Transparent redirector of any TCP/UDP connection to proxy. +endef + +MAKE_VARS += DISABLE_SHADOWSOCKS=true + +define Package/redsocks2/install + $(INSTALL_DIR) $(1)/usr/sbin + $(INSTALL_BIN) $(PKG_BUILD_DIR)/redsocks2 $(1)/usr/sbin + $(INSTALL_DIR) $(1)/etc/redsocks2 + $(INSTALL_DATA) ./files/redsocks2.template $(1)/etc/redsocks2/config.template +endef + +$(eval $(call BuildPackage,redsocks2)) diff --git a/redsocks2/files/redsocks2.template b/redsocks2/files/redsocks2.template new file mode 100644 index 000000000..ba494267e --- /dev/null +++ b/redsocks2/files/redsocks2.template @@ -0,0 +1,33 @@ +base { + log_debug = off; + redirector = iptables; + reuseport = on; +} +redsocks { + local_ip = 192.168.1.1; + local_port = 1081; + ip = 192.168.1.1; + port = 9050; + type = socks5; // I use socks5 proxy for GFW'ed IP + autoproxy = 1; // I want autoproxy feature enabled on this section. + // timeout is meaningful when 'autoproxy' is non-zero. + // It specified timeout value when trying to connect to destination + // directly. Default is 10 seconds. When it is set to 0, default + // timeout value will be used. + // NOTE: decreasing the timeout value may lead increase of chance for + // normal IP to be misjudged. + timeout = 13; + //type = http-connect; + //login = username; + //password = passwd; +} +tcpdns { + // Transform UDP DNS requests into TCP DNS requests. + // You can also redirect connections to external TCP DNS server to + // REDSOCKS transparent proxy via iptables. + local_ip = 192.168.1.1; // Local server to act as DNS server + local_port = 1053; // UDP port to receive UDP DNS requests + tcpdns1 = 8.8.4.4; // DNS server that supports TCP DNS requests + tcpdns2 = 8.8.8.8; // DNS server that supports TCP DNS requests + timeout = 4; // Timeout value for TCP DNS requests +} diff --git a/shadow-tls/Makefile b/shadow-tls/Makefile new file mode 100644 index 000000000..2b70c4e5b --- /dev/null +++ b/shadow-tls/Makefile @@ -0,0 +1,69 @@ +# SPDX-License-Identifier: GPL-2.0-only + +include $(TOPDIR)/rules.mk +include $(INCLUDE_DIR)/package.mk + +PKG_NAME:=shadow-tls +PKG_VERSION:=0.2.25 +PKG_RELEASE:=1 + +RELEASE_HEAD:=$(PKG_NAME) +RELEASE_FOOT:=unknown-linux-musl +ifeq ($(ARCH),aarch64) + PKG_ARCH:=$(RELEASE_HEAD)-aarch64-$(RELEASE_FOOT) + PKG_HASH:=3295476b37f549a68906519d3eaecb74bf3b6eaf9094cebb16ee84f0151373c6 +else ifeq ($(ARCH),arm) + ifeq ($(CONFIG_CPU_TYPE),cortex-a7) + PKG_ARCH:=$(RELEASE_HEAD)-armv7-$(RELEASE_FOOT)eabihf + PKG_HASH:=e6f918a072557c50fd0ea950af9a156a9b102af72c1d010ff85d08d13006c54f + else ifeq ($(CONFIG_CPU_TYPE),cortex-a9) + PKG_ARCH:=$(RELEASE_HEAD)-armv7-$(RELEASE_FOOT)eabihf + PKG_HASH:=e6f918a072557c50fd0ea950af9a156a9b102af72c1d010ff85d08d13006c54f + else + PKG_ARCH:=$(RELEASE_HEAD)-arm-$(RELEASE_FOOT)eabi + PKG_HASH:=b6743bc60e1727972ece0fd5acf3a931e5be05cedee6f637e7e3d8c5b8d58f16 + endif +else ifeq ($(ARCH),x86_64) + PKG_ARCH:=$(RELEASE_HEAD)-x86_64-$(RELEASE_FOOT) + PKG_HASH:=a173f5f2d57f45211b68e10ceeddc15b1791077b914fa89747bc705fddc71532 +else + PKG_SOURCE:=dummy + PKG_HASH:=dummy +endif + +PKG_LICENSE:=GPL-3.0 +PKG_LICENSE_FILE:=LICENSE +PKG_MAINTAINER:=Tianling Shen + +define Download/shadow-tls + URL:=https://github.com/ihciah/shadow-tls/releases/download/v$(PKG_VERSION) + URL_FILE:=$(PKG_ARCH) + FILE:=$(PKG_ARCH) + HASH:=$(PKG_HASH) +endef + +define Package/shadow-tls + SECTION:=net + CATEGORY:=Network + SUBMENU:=Web Servers/Proxies + TITLE:=A proxy to expose real tls handshake to the firewall. + URL:=https://github.com/ihciah/shadow-tls + DEPENDS:=@USE_MUSL @(aarch64||arm||x86_64) @!(TARGET_x86_geode||TARGET_x86_legacy) +endef + +define Build/Prepare + $(call Build/Prepare/Default) +ifneq ($(CONFIG_PACKAGE_shadow-tls),) + $(call Download,shadow-tls) +endif +endef + +define Build/Compile +endef + +define Package/shadow-tls/install + $(INSTALL_DIR) $(1)/usr/bin + $(INSTALL_BIN) $(DL_DIR)/$(PKG_ARCH) $(1)/usr/bin/shadow-tls +endef + +$(eval $(call BuildPackage,shadow-tls)) diff --git a/shadowsocks-rust/Makefile b/shadowsocks-rust/Makefile new file mode 100644 index 000000000..e0e8e32d5 --- /dev/null +++ b/shadowsocks-rust/Makefile @@ -0,0 +1,90 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Copyright (C) 2017-2020 Yousong Zhou +# Copyright (C) 2021 ImmortalWrt.org + +include $(TOPDIR)/rules.mk + +PKG_NAME:=shadowsocks-rust +PKG_VERSION:=1.24.0 +PKG_RELEASE:=1 + +PKG_SOURCE_HEADER:=shadowsocks-v$(PKG_VERSION) +PKG_SOURCE_BODY:=unknown-linux-musl +PKG_SOURCE_FOOTER:=tar.xz + +ifeq ($(filter $(ARCH),mips mipsel),) + PKG_SOURCE_URL:=https://github.com/shadowsocks/shadowsocks-rust/releases/download/v$(PKG_VERSION)/ +else + PKG_SOURCE_URL:=https://github.com/sbwml/shadowsocks-rust-mips/releases/download/v$(PKG_VERSION)/ +endif + +ifeq ($(ARCH),aarch64) + PKG_SOURCE:=$(PKG_SOURCE_HEADER).aarch64-$(PKG_SOURCE_BODY).$(PKG_SOURCE_FOOTER) + PKG_HASH:=e00b6551f40bb2d61adb2503909e0df6550c022372c812f3f34350510797ef2f +else ifeq ($(ARCH),arm) + # Referred to golang/golang-values.mk + ARM_CPU_FEATURES:=$(word 2,$(subst +,$(space),$(call qstrip,$(CONFIG_CPU_TYPE)))) + ifeq ($(ARM_CPU_FEATURES),) + PKG_SOURCE:=$(PKG_SOURCE_HEADER).arm-$(PKG_SOURCE_BODY)eabi.$(PKG_SOURCE_FOOTER) + PKG_HASH:=b00694ac484eaf994408c874c70e1d3392d1654cf3d9391ddf2b589bbee9106c + else + PKG_SOURCE:=$(PKG_SOURCE_HEADER).arm-$(PKG_SOURCE_BODY)eabihf.$(PKG_SOURCE_FOOTER) + PKG_HASH:=db56c8e64ce3651907c31fe6a585a68e4c4576c8379f50d82be31d79ba8d00ad + endif +else ifeq ($(ARCH),i386) + PKG_SOURCE:=$(PKG_SOURCE_HEADER).i686-$(PKG_SOURCE_BODY).$(PKG_SOURCE_FOOTER) + PKG_HASH:=a9aabb4209a8f29afabddb2aaaa8a38d8f604fd0075250d61bd594bb10ae38c7 +else ifeq ($(ARCH),x86_64) + PKG_SOURCE:=$(PKG_SOURCE_HEADER).x86_64-$(PKG_SOURCE_BODY).$(PKG_SOURCE_FOOTER) + PKG_HASH:=0d84f5f350ec99396867d718f146fc3810975b2a7cd06192f158d96bdef460e7 +else ifeq ($(ARCH),mips) + PKG_SOURCE:=$(PKG_SOURCE_HEADER).mips-$(PKG_SOURCE_BODY).$(PKG_SOURCE_FOOTER) + PKG_HASH:=40319c20934121e8d5df90e54cd2b41e064a3487f180f6924ac42ba3cbcebe4f +else ifeq ($(ARCH),mipsel) + PKG_SOURCE:=$(PKG_SOURCE_HEADER).mipsel-$(PKG_SOURCE_BODY).$(PKG_SOURCE_FOOTER) + PKG_HASH:=d51792a5b4f6a28bb2e8b07e2f39a60535d1ede9d1973a257c5751a991e26ac8 +# Set the default value to make OpenWrt Package Checker happy +else + PKG_SOURCE:=dummy + PKG_HASH:=dummy +endif + +PKG_MAINTAINER:=Tianling Shen +PKG_LICENSE:=MIT +PKG_LICENSE_FILES:=LICENSE + +include $(INCLUDE_DIR)/package.mk + +TAR_CMD:=$(HOST_TAR) -C $(PKG_BUILD_DIR) $(TAR_OPTIONS) + +define Package/shadowsocks-rust/Default + define Package/shadowsocks-rust-$(1) + SECTION:=net + CATEGORY:=Network + SUBMENU:=Web Servers/Proxies + TITLE:=shadowsocks-rust $(1) + URL:=https://github.com/shadowsocks/shadowsocks-rust + DEPENDS:=@(aarch64||arm||i386||mips||mipsel||x86_64) @!(TARGET_x86_geode||TARGET_x86_legacy) + endef + + define Package/shadowsocks-rust-$(1)/install + $$(INSTALL_DIR) $$(1)/usr/bin + $$(INSTALL_BIN) $$(PKG_BUILD_DIR)/$(1) $$(1)/usr/bin + endef +endef + +SHADOWSOCKS_COMPONENTS:=sslocal ssmanager ssserver ssurl ssservice +define shadowsocks-rust/templates + $(foreach component,$(SHADOWSOCKS_COMPONENTS), + $(call Package/shadowsocks-rust/Default,$(component)) + ) +endef +$(eval $(call shadowsocks-rust/templates)) + +define Build/Compile +endef + +$(foreach component,$(SHADOWSOCKS_COMPONENTS), \ + $(eval $(call BuildPackage,shadowsocks-rust-$(component))) \ +) diff --git a/shadowsocksr-libev/Makefile b/shadowsocksr-libev/Makefile new file mode 100644 index 000000000..2235e75db --- /dev/null +++ b/shadowsocksr-libev/Makefile @@ -0,0 +1,57 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=shadowsocksr-libev +PKG_VERSION:=2.5.3 +PKG_RELEASE:=1 + +PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz +PKG_SOURCE_URL:=https://codeload.github.com/shadowsocksrr/shadowsocksr-libev/tar.gz/$(PKG_VERSION)? +PKG_HASH:=3fc11a0a132aa6f229285f459e8ce391c20bf86b731b757b0de4dbc96c12301c + +PKG_LICENSE:=GPL-3.0 +PKG_LICENSE_FILES:=LICENSE + +PKG_FIXUP:=autoreconf +PKG_USE_MIPS16:=0 +PKG_BUILD_FLAGS:=no-mips16 +PKG_BUILD_PARALLEL:=1 +PKG_INSTALL:=1 + +include $(INCLUDE_DIR)/package.mk + +define Package/shadowsocksr-libev/Default + define Package/shadowsocksr-libev-ssr-$(1) + SECTION:=net + CATEGORY:=Network + SUBMENU:=Web Servers/Proxies + TITLE:=shadowsocksr-libev ssr-$(1) + URL:=https://github.com/shadowsocksrr/shadowsocksr-libev + DEPENDS:=+libev +libsodium +libopenssl +libpthread +libpcre2 +libudns +zlib +libopenssl-legacy + endef + + define Package/shadowsocksr-libev-ssr-$(1)/install + $$(INSTALL_DIR) $$(1)/usr/bin + $$(INSTALL_BIN) $$(PKG_INSTALL_DIR)/usr/bin/ss-$(1) $$(1)/usr/bin/ssr-$(1) + endef +endef + +SHADOWSOCKSR_COMPONENTS:=check local nat redir server +define shadowsocksr-libev/templates + $(foreach component,$(SHADOWSOCKSR_COMPONENTS), + $(call Package/shadowsocksr-libev/Default,$(component)) + ) +endef +$(eval $(call shadowsocksr-libev/templates)) + +CONFIGURE_ARGS += \ + --disable-documentation \ + --disable-ssp \ + --disable-assert \ + --enable-system-shared-lib + +TARGET_CFLAGS += -flto +TARGET_LDFLAGS += -Wl,--gc-sections,--as-needed + +$(foreach component,$(SHADOWSOCKSR_COMPONENTS), \ + $(eval $(call BuildPackage,shadowsocksr-libev-ssr-$(component))) \ +) \ No newline at end of file diff --git a/shadowsocksr-libev/patches/0001-Add-ss-server-and-ss-check.patch b/shadowsocksr-libev/patches/0001-Add-ss-server-and-ss-check.patch new file mode 100644 index 000000000..290ede540 --- /dev/null +++ b/shadowsocksr-libev/patches/0001-Add-ss-server-and-ss-check.patch @@ -0,0 +1,397 @@ +--- a/.gitignore ++++ b/.gitignore +@@ -2,6 +2,7 @@ build/ + .deps/ + /Makefile + src/Makefile ++server/Makefile + libev/Makefile + libudns/Makefile + libcork/Makefile +--- a/Makefile.am ++++ b/Makefile.am +@@ -1,7 +1,7 @@ + if USE_SYSTEM_SHARED_LIB +-SUBDIRS = libcork libipset src ++SUBDIRS = libcork libipset src server + else +-SUBDIRS = libsodium libcork libipset libudns libev src ++SUBDIRS = libsodium libcork libipset libudns libev src server + endif + + if ENABLE_DOCUMENTATION +--- a/Makefile.in ++++ b/Makefile.in +@@ -195,7 +195,7 @@ am__define_uniq_tagged_files = \ + ETAGS = etags + CTAGS = ctags + CSCOPE = cscope +-DIST_SUBDIRS = libsodium libcork libipset libudns libev src doc ++DIST_SUBDIRS = libsodium libcork libipset libudns libev src server doc + am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/config.h.in \ + $(srcdir)/shadowsocks-libev.pc.in $(top_srcdir)/auto/ar-lib \ + $(top_srcdir)/auto/compile $(top_srcdir)/auto/config.guess \ +@@ -377,8 +377,9 @@ top_build_prefix = @top_build_prefix@ + top_builddir = @top_builddir@ + top_srcdir = @top_srcdir@ + @USE_SYSTEM_SHARED_LIB_FALSE@SUBDIRS = libsodium libcork libipset \ +-@USE_SYSTEM_SHARED_LIB_FALSE@ libudns libev src $(am__append_1) +-@USE_SYSTEM_SHARED_LIB_TRUE@SUBDIRS = libcork libipset src \ ++@USE_SYSTEM_SHARED_LIB_FALSE@ libudns libev src server \ ++@USE_SYSTEM_SHARED_LIB_FALSE@ $(am__append_1) ++@USE_SYSTEM_SHARED_LIB_TRUE@SUBDIRS = libcork libipset src server \ + @USE_SYSTEM_SHARED_LIB_TRUE@ $(am__append_1) + ACLOCAL_AMFLAGS = -I m4 + pkgconfiglibdir = $(libdir)/pkgconfig +--- a/configure ++++ b/configure +@@ -649,7 +649,6 @@ PTHREAD_CC + ax_pthread_config + INET_NTOP_LIB + MV +-RM + GZIP + XMLTO + ASCIIDOC +@@ -757,6 +756,7 @@ infodir + docdir + oldincludedir + includedir ++runstatedir + localstatedir + sharedstatedir + sysconfdir +@@ -857,6 +857,7 @@ datadir='${datarootdir}' + sysconfdir='${prefix}/etc' + sharedstatedir='${prefix}/com' + localstatedir='${prefix}/var' ++runstatedir='${localstatedir}/run' + includedir='${prefix}/include' + oldincludedir='/usr/include' + docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' +@@ -1109,6 +1110,15 @@ do + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + ++ -runstatedir | --runstatedir | --runstatedi | --runstated \ ++ | --runstate | --runstat | --runsta | --runst | --runs \ ++ | --run | --ru | --r) ++ ac_prev=runstatedir ;; ++ -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \ ++ | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \ ++ | --run=* | --ru=* | --r=*) ++ runstatedir=$ac_optarg ;; ++ + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ +@@ -1246,7 +1256,7 @@ fi + for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ + datadir sysconfdir sharedstatedir localstatedir includedir \ + oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ +- libdir localedir mandir ++ libdir localedir mandir runstatedir + do + eval ac_val=\$$ac_var + # Remove trailing slashes. +@@ -1399,6 +1409,7 @@ Fine tuning of the installation director + --sysconfdir=DIR read-only single-machine data [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] + --localstatedir=DIR modifiable single-machine data [PREFIX/var] ++ --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run] + --libdir=DIR object code libraries [EPREFIX/lib] + --includedir=DIR C header files [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc [/usr/include] +@@ -2472,8 +2483,8 @@ ac_configure="$SHELL $ac_aux_dir/configu + + + +-# expand $ac_aux_dir to an absolute path +-am_aux_dir=`cd $ac_aux_dir && pwd` ++# Expand $ac_aux_dir to an absolute path. ++am_aux_dir=`cd "$ac_aux_dir" && pwd` + + ac_ext=c + ac_cpp='$CPP $CPPFLAGS' +@@ -3783,7 +3794,7 @@ $as_echo "$ac_cv_safe_to_define___extens + + + +-am__api_version='1.14' ++am__api_version='1.15' + + # Find a good install program. We prefer a C program (faster), + # so one script is as good as another. But avoid the broken or +@@ -3972,7 +3983,7 @@ else + $as_echo "$as_me: WARNING: 'missing' script is too old or missing" >&2;} + fi + +-if test x"${install_sh}" != xset; then ++if test x"${install_sh+set}" != xset; then + case $am_aux_dir in + *\ * | *\ *) + install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; +@@ -4363,8 +4374,8 @@ MAKEINFO=${MAKEINFO-"${am_missing_run}ma + # + mkdir_p='$(MKDIR_P)' + +-# We need awk for the "check" target. The system "awk" is bad on +-# some platforms. ++# We need awk for the "check" target (and possibly the TAP driver). The ++# system "awk" is bad on some platforms. + # Always define AMTAR for backward compatibility. Yes, it's still used + # in the wild :-( We should find a proper way to deprecate it ... + AMTAR='$${TAR-tar}' +@@ -4549,6 +4560,7 @@ END + as_fn_error $? "Your 'rm' program is bad, sorry." "$LINENO" 5 + fi + fi ++ + if test -n "$ac_tool_prefix"; then + for ac_prog in ar lib "link -lib" + do +@@ -12494,47 +12506,6 @@ $as_echo "no" >&6; } + fi + + +- # Extract the first word of "rm", so it can be a program name with args. +-set dummy rm; ac_word=$2 +-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +-$as_echo_n "checking for $ac_word... " >&6; } +-if ${ac_cv_path_RM+:} false; then : +- $as_echo_n "(cached) " >&6 +-else +- case $RM in +- [\\/]* | ?:[\\/]*) +- ac_cv_path_RM="$RM" # Let the user override the test with a path. +- ;; +- *) +- as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +-for as_dir in $PATH +-do +- IFS=$as_save_IFS +- test -z "$as_dir" && as_dir=. +- for ac_exec_ext in '' $ac_executable_extensions; do +- if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then +- ac_cv_path_RM="$as_dir/$ac_word$ac_exec_ext" +- $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 +- break 2 +- fi +-done +- done +-IFS=$as_save_IFS +- +- test -z "$ac_cv_path_RM" && ac_cv_path_RM="rm" +- ;; +-esac +-fi +-RM=$ac_cv_path_RM +-if test -n "$RM"; then +- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RM" >&5 +-$as_echo "$RM" >&6; } +-else +- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +-$as_echo "no" >&6; } +-fi +- +- + # Extract the first word of "mv", so it can be a program name with args. + set dummy mv; ac_word=$2 + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +@@ -16204,15 +16175,162 @@ $as_echo "#define HAVE_IPv6 1" >>confdef + + + if test -z "$USE_SYSTEM_SHARED_LIB_TRUE"; then : +- else ++ ++ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for sodium_init in -lsodium" >&5 ++$as_echo_n "checking for sodium_init in -lsodium... " >&6; } ++if ${ac_cv_lib_sodium_sodium_init+:} false; then : ++ $as_echo_n "(cached) " >&6 ++else ++ ac_check_lib_save_LIBS=$LIBS ++LIBS="-lsodium $LIBS" ++cat confdefs.h - <<_ACEOF >conftest.$ac_ext ++/* end confdefs.h. */ ++ ++/* Override any GCC internal prototype to avoid an error. ++ Use char because int might match the return type of a GCC ++ builtin and then its argument prototype would still apply. */ ++#ifdef __cplusplus ++extern "C" ++#endif ++char sodium_init (); ++int ++main () ++{ ++return sodium_init (); ++ ; ++ return 0; ++} ++_ACEOF ++if ac_fn_c_try_link "$LINENO"; then : ++ ac_cv_lib_sodium_sodium_init=yes ++else ++ ac_cv_lib_sodium_sodium_init=no ++fi ++rm -f core conftest.err conftest.$ac_objext \ ++ conftest$ac_exeext conftest.$ac_ext ++LIBS=$ac_check_lib_save_LIBS ++fi ++{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_sodium_sodium_init" >&5 ++$as_echo "$ac_cv_lib_sodium_sodium_init" >&6; } ++if test "x$ac_cv_lib_sodium_sodium_init" = xyes; then : ++ cat >>confdefs.h <<_ACEOF ++#define HAVE_LIBSODIUM 1 ++_ACEOF ++ ++ LIBS="-lsodium $LIBS" ++ ++else ++ ++ as_fn_error $? "Couldn't find libsodium. Try installing libsodium-dev[el]." "$LINENO" 5 ++ ++fi ++ ++ ++else + subdirs="$subdirs libsodium" + + fi + +-ac_config_files="$ac_config_files shadowsocks-libev.pc Makefile libcork/Makefile libipset/Makefile src/Makefile" ++ac_config_files="$ac_config_files shadowsocks-libev.pc Makefile libcork/Makefile libipset/Makefile src/Makefile server/Makefile" + + if test -z "$USE_SYSTEM_SHARED_LIB_TRUE"; then : +- else ++ ++ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dns_dnlen in -ludns" >&5 ++$as_echo_n "checking for dns_dnlen in -ludns... " >&6; } ++if ${ac_cv_lib_udns_dns_dnlen+:} false; then : ++ $as_echo_n "(cached) " >&6 ++else ++ ac_check_lib_save_LIBS=$LIBS ++LIBS="-ludns $LIBS" ++cat confdefs.h - <<_ACEOF >conftest.$ac_ext ++/* end confdefs.h. */ ++ ++/* Override any GCC internal prototype to avoid an error. ++ Use char because int might match the return type of a GCC ++ builtin and then its argument prototype would still apply. */ ++#ifdef __cplusplus ++extern "C" ++#endif ++char dns_dnlen (); ++int ++main () ++{ ++return dns_dnlen (); ++ ; ++ return 0; ++} ++_ACEOF ++if ac_fn_c_try_link "$LINENO"; then : ++ ac_cv_lib_udns_dns_dnlen=yes ++else ++ ac_cv_lib_udns_dns_dnlen=no ++fi ++rm -f core conftest.err conftest.$ac_objext \ ++ conftest$ac_exeext conftest.$ac_ext ++LIBS=$ac_check_lib_save_LIBS ++fi ++{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_udns_dns_dnlen" >&5 ++$as_echo "$ac_cv_lib_udns_dns_dnlen" >&6; } ++if test "x$ac_cv_lib_udns_dns_dnlen" = xyes; then : ++ cat >>confdefs.h <<_ACEOF ++#define HAVE_LIBUDNS 1 ++_ACEOF ++ ++ LIBS="-ludns $LIBS" ++ ++else ++ as_fn_error $? "Couldn't find libudns. Try installing libudns-dev or udns-devel." "$LINENO" 5 ++fi ++ ++ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ev_loop_destroy in -lev" >&5 ++$as_echo_n "checking for ev_loop_destroy in -lev... " >&6; } ++if ${ac_cv_lib_ev_ev_loop_destroy+:} false; then : ++ $as_echo_n "(cached) " >&6 ++else ++ ac_check_lib_save_LIBS=$LIBS ++LIBS="-lev $LIBS" ++cat confdefs.h - <<_ACEOF >conftest.$ac_ext ++/* end confdefs.h. */ ++ ++/* Override any GCC internal prototype to avoid an error. ++ Use char because int might match the return type of a GCC ++ builtin and then its argument prototype would still apply. */ ++#ifdef __cplusplus ++extern "C" ++#endif ++char ev_loop_destroy (); ++int ++main () ++{ ++return ev_loop_destroy (); ++ ; ++ return 0; ++} ++_ACEOF ++if ac_fn_c_try_link "$LINENO"; then : ++ ac_cv_lib_ev_ev_loop_destroy=yes ++else ++ ac_cv_lib_ev_ev_loop_destroy=no ++fi ++rm -f core conftest.err conftest.$ac_objext \ ++ conftest$ac_exeext conftest.$ac_ext ++LIBS=$ac_check_lib_save_LIBS ++fi ++{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ev_ev_loop_destroy" >&5 ++$as_echo "$ac_cv_lib_ev_ev_loop_destroy" >&6; } ++if test "x$ac_cv_lib_ev_ev_loop_destroy" = xyes; then : ++ cat >>confdefs.h <<_ACEOF ++#define HAVE_LIBEV 1 ++_ACEOF ++ ++ LIBS="-lev $LIBS" ++ ++else ++ as_fn_error $? "Couldn't find libev. Try installing libev-dev[el]." "$LINENO" 5 ++fi ++ ++ ++else + ac_config_files="$ac_config_files libudns/Makefile libev/Makefile" + + fi +@@ -17258,6 +17376,7 @@ do + "libcork/Makefile") CONFIG_FILES="$CONFIG_FILES libcork/Makefile" ;; + "libipset/Makefile") CONFIG_FILES="$CONFIG_FILES libipset/Makefile" ;; + "src/Makefile") CONFIG_FILES="$CONFIG_FILES src/Makefile" ;; ++ "server/Makefile") CONFIG_FILES="$CONFIG_FILES server/Makefile" ;; + "libudns/Makefile") CONFIG_FILES="$CONFIG_FILES libudns/Makefile" ;; + "libev/Makefile") CONFIG_FILES="$CONFIG_FILES libev/Makefile" ;; + "doc/Makefile") CONFIG_FILES="$CONFIG_FILES doc/Makefile" ;; +@@ -17958,8 +18077,8 @@ $as_echo X"$file" | + fi + + cfgfile="${ofile}T" +- trap "$RM -f \"$cfgfile\"; exit 1" 1 2 15 +- $RM -f "$cfgfile" ++ trap "$RM \"$cfgfile\"; exit 1" 1 2 15 ++ $RM "$cfgfile" + + cat <<_LT_EOF >> "$cfgfile" + #! $SHELL +--- a/configure.ac ++++ b/configure.ac +@@ -315,7 +315,8 @@ AC_CONFIG_FILES([ shadowsocks-libev.pc + Makefile + libcork/Makefile + libipset/Makefile +- src/Makefile]) ++ src/Makefile ++ server/Makefile]) + AM_COND_IF([USE_SYSTEM_SHARED_LIB],[ + AC_CHECK_LIB([udns], [dns_dnlen], ,[AC_MSG_ERROR([Couldn't find libudns. Try installing libudns-dev or udns-devel.])]) + AC_CHECK_LIB([ev], [ev_loop_destroy], ,[AC_MSG_ERROR([Couldn't find libev. Try installing libev-dev@<:@el@:>@.])]) diff --git a/shadowsocksr-libev/patches/0002-Revert-verify_simple-and-auth_simple.patch b/shadowsocksr-libev/patches/0002-Revert-verify_simple-and-auth_simple.patch new file mode 100644 index 000000000..0f8114656 --- /dev/null +++ b/shadowsocksr-libev/patches/0002-Revert-verify_simple-and-auth_simple.patch @@ -0,0 +1,20 @@ +--- a/src/obfs/obfs.c ++++ b/src/obfs/obfs.c +@@ -88,7 +88,7 @@ obfs_class *new_obfs_class(const char *p + plugin->client_decode = tls12_ticket_auth_client_decode; + + return plugin; +- /*} else if (strcmp(plugin_name, "verify_simple") == 0) { ++ } else if (strcmp(plugin_name, "verify_simple") == 0) { + obfs_class * plugin = (obfs_class*)malloc(sizeof(obfs_class)); + plugin->init_data = init_data; + plugin->new_obfs = verify_simple_new_obfs; +@@ -115,7 +115,7 @@ obfs_class *new_obfs_class(const char *p + plugin->client_udp_pre_encrypt = NULL; + plugin->client_udp_post_decrypt = NULL; + +- return plugin;*/ ++ return plugin; + } else if (strcmp(plugin_name, "auth_sha1") == 0) { + obfs_class *plugin = (obfs_class *) malloc(sizeof(obfs_class)); + plugin->init_data = auth_simple_init_data; diff --git a/shadowsocksr-libev/patches/0003-Refine-Usage.patch b/shadowsocksr-libev/patches/0003-Refine-Usage.patch new file mode 100644 index 000000000..1d8d7f293 --- /dev/null +++ b/shadowsocksr-libev/patches/0003-Refine-Usage.patch @@ -0,0 +1,37 @@ +--- a/src/utils.c ++++ b/src/utils.c +@@ -258,8 +258,6 @@ usage() + { + printf("\n"); + printf("shadowsocks-libev %s with %s\n\n", VERSION, USING_CRYPTO); +- printf( +- " maintained by Max Lv and Linus Yang \n\n"); + printf(" usage:\n\n"); + #ifdef MODULE_LOCAL + printf(" ss-local\n"); +@@ -299,6 +297,25 @@ usage() + " The default cipher is rc4-md5.\n"); + printf("\n"); + printf( ++ " -o Obfs of your remote server: plain,\n"); ++ printf( ++ " http_simple, http_post and tls1.2_ticket_auth.\n"); ++ printf( ++ " -g Obfs-Param of your remote server.\n"); ++ printf( ++ " -O Protocol of your remote server: origin,\n"); ++ printf( ++ " auth_sha1, auth_sha1_v2, auth_sha1_v4,\n"); ++ printf( ++ " auth_aes128_md5, auth_aes128_sha1,\n"); ++ printf( ++ " auth_chain_a, auth_chain_b, auth_chain_c,\n"); ++ printf( ++ " auth_chain_d, auth_chain_e and auth_chain_f.\n"); ++ printf( ++ " -G Protocol-Param of your remote server.\n"); ++ printf("\n"); ++ printf( + " [-a ] Run as another user.\n"); + printf( + " [-f ] The file path to store pid.\n"); diff --git a/shadowsocksr-libev/patches/100-fix-gcc-10.patch b/shadowsocksr-libev/patches/100-fix-gcc-10.patch new file mode 100644 index 000000000..1049fc2f8 --- /dev/null +++ b/shadowsocksr-libev/patches/100-fix-gcc-10.patch @@ -0,0 +1,20 @@ +--- a/src/http.h ++++ b/src/http.h +@@ -29,6 +29,6 @@ + #include + #include "protocol.h" + +-const protocol_t *const http_protocol; ++extern const protocol_t *const http_protocol; + + #endif +--- a/src/tls.h ++++ b/src/tls.h +@@ -28,6 +28,6 @@ + + #include "protocol.h" + +-const protocol_t *const tls_protocol; ++extern const protocol_t *const tls_protocol; + + #endif diff --git a/shadowsocksr-libev/patches/101-Fix-Werror-sizeof-pointer-memaccess.patch b/shadowsocksr-libev/patches/101-Fix-Werror-sizeof-pointer-memaccess.patch new file mode 100644 index 000000000..1db501aab --- /dev/null +++ b/shadowsocksr-libev/patches/101-Fix-Werror-sizeof-pointer-memaccess.patch @@ -0,0 +1,11 @@ +--- a/src/local.c ++++ b/src/local.c +@@ -718,7 +718,7 @@ server_recv_cb(EV_P_ ev_io *w, int reven + + ss_free(hostname); + } else { +- strncpy(host, ip, sizeof(ip)); ++ strncpy(host, ip, INET6_ADDRSTRLEN); + } + } + diff --git a/shadowsocksr-libev/patches/102-Read-listening-mode-from-config.patch b/shadowsocksr-libev/patches/102-Read-listening-mode-from-config.patch new file mode 100644 index 000000000..57f6f76dc --- /dev/null +++ b/shadowsocksr-libev/patches/102-Read-listening-mode-from-config.patch @@ -0,0 +1,34 @@ +--- a/src/jconf.c ++++ b/src/jconf.c +@@ -259,6 +259,19 @@ read_jconf(const char *file) + conf.server_legacy.obfs = to_string(value); + } else if (strcmp(name, "obfs_param") == 0) { // SSR + conf.server_legacy.obfs_param = to_string(value); ++ } else if (strcmp(name, "mode") == 0) { ++ char *mode_str = to_string(value); ++ ++ if (strcmp(mode_str, "tcp_only") == 0) ++ conf.mode = TCP_ONLY; ++ else if (strcmp(mode_str, "tcp_and_udp") == 0) ++ conf.mode = TCP_AND_UDP; ++ else if (strcmp(mode_str, "udp_only") == 0) ++ conf.mode = UDP_ONLY; ++ else ++ LOGI("ignore unknown mode: %s, use tcp_only as fallback", ++ mode_str); ++ ss_free(mode_str); + } else { + match = 0; + } +--- a/src/redir.c ++++ b/src/redir.c +@@ -1252,6 +1252,9 @@ main(int argc, char **argv) + if (user == NULL) { + user = conf->user; + } ++ if (mode == TCP_ONLY) { ++ mode = conf->mode; ++ } + if (mtu == 0) { + mtu = conf->mtu; + } diff --git a/shadowsocksr-libev/patches/103-Add-TPROXY-support-for-TCP-ssr-redir.patch b/shadowsocksr-libev/patches/103-Add-TPROXY-support-for-TCP-ssr-redir.patch new file mode 100644 index 000000000..317d819db --- /dev/null +++ b/shadowsocksr-libev/patches/103-Add-TPROXY-support-for-TCP-ssr-redir.patch @@ -0,0 +1,154 @@ +--- a/completions/bash/ss-redir ++++ b/completions/bash/ss-redir +@@ -2,7 +2,7 @@ _ss_redir() + { + local cur prev opts ciphers + ciphers='rc4-md5 table rc4 aes-128-cfb aes-192-cfb aes-256-cfb aes-128-ctr aes-192-ctr aes-256-ctr bf-cfb camellia-128-cfb camellia-192-cfb camellia-256-cfb cast5-cfb des-cfb idea-cfb rc2-cfb seed-cfb salsa20 chacha20 and chacha20-ietf' +- opts='-s -b -p -k -f -t -m -c -a -n -u -U -v -h -A --mtu --help --mptcp -l' ++ opts='-s -b -p -k -f -t -m -c -a -n -u -U -T -v -h -A --mtu --help --mptcp -l' + cur=${COMP_WORDS[COMP_CWORD]} + prev="${COMP_WORDS[COMP_CWORD-1]}" + case "$prev" in +--- a/src/jconf.c ++++ b/src/jconf.c +@@ -338,7 +338,11 @@ read_jconf(const char *file) + check_json_value_type(value, json_boolean, + "invalid config file: option 'ipv6_first' must be a boolean"); + conf.ipv6_first = value->u.boolean; +- } ++ } else if (strcmp(name, "tcp_tproxy") == 0) { ++ check_json_value_type(value, json_boolean, ++ "invalid config file: option 'tcp_tproxy' must be a boolean"); ++ conf.tcp_tproxy = value->u.boolean; ++ } + } + } + } else { +--- a/src/jconf.h ++++ b/src/jconf.h +@@ -105,6 +105,7 @@ typedef struct { + int mtu; + int mptcp; + int ipv6_first; ++ int tcp_tproxy; + } jconf_t; + + jconf_t *read_jconf(const char *file); +--- a/src/redir.c ++++ b/src/redir.c +@@ -71,6 +71,14 @@ + #define IP6T_SO_ORIGINAL_DST 80 + #endif + ++#ifndef IP_TRANSPARENT ++#define IP_TRANSPARENT 19 ++#endif ++ ++#ifndef IPV6_TRANSPARENT ++#define IPV6_TRANSPARENT 75 ++#endif ++ + #include "includeobfs.h" // I don't want to modify makefile + #include "jconf.h" + +@@ -101,18 +109,28 @@ static struct cork_dllist inactive_profi + static listen_ctx_t *current_profile; + static struct cork_dllist all_connections; + ++static int tcp_tproxy = 0; /* use tproxy instead of redirect (for tcp) */ ++ + int + getdestaddr(int fd, struct sockaddr_storage *destaddr) + { + socklen_t socklen = sizeof(*destaddr); + int error = 0; + +- error = getsockopt(fd, SOL_IPV6, IP6T_SO_ORIGINAL_DST, destaddr, &socklen); +- if (error) { // Didn't find a proper way to detect IP version. +- error = getsockopt(fd, SOL_IP, SO_ORIGINAL_DST, destaddr, &socklen); +- if (error) { +- return -1; +- } ++ if (tcp_tproxy) { ++ error = getsockname(fd, (void *)destaddr, &socklen); ++ } else { ++ error = getsockopt(fd, SOL_IPV6, IP6T_SO_ORIGINAL_DST, destaddr, &socklen); ++ if (error) { // Didn't find a proper way to detect IP version. ++ error = getsockopt(fd, SOL_IP, SO_ORIGINAL_DST, destaddr, &socklen); ++ if (error) { ++ return -1; ++ } ++ } ++ } ++ ++ if (error) { ++ return -1; + } + return 0; + } +@@ -164,6 +182,23 @@ create_and_bind(const char *addr, const + if (err == 0) { + LOGI("tcp port reuse enabled"); + } ++ ++ if (tcp_tproxy) { ++ int level = 0, optname = 0; ++ if (rp->ai_family == AF_INET) { ++ level = IPPROTO_IP; ++ optname = IP_TRANSPARENT; ++ } else { ++ level = IPPROTO_IPV6; ++ optname = IPV6_TRANSPARENT; ++ } ++ ++ if (setsockopt(listen_sock, level, optname, &opt, sizeof(opt)) != 0) { ++ ERROR("setsockopt IP_TRANSPARENT"); ++ exit(EXIT_FAILURE); ++ } ++ LOGI("tcp tproxy mode enabled"); ++ } + + s = bind(listen_sock, rp->ai_addr, rp->ai_addrlen); + if (s == 0) { +@@ -1094,7 +1129,7 @@ main(int argc, char **argv) + + USE_TTY(); + +- while ((c = getopt_long(argc, argv, "f:s:p:l:k:t:m:c:b:a:n:huUvA6" ++ while ((c = getopt_long(argc, argv, "f:s:p:l:k:t:m:c:b:a:n:huUTvA6" + "O:o:G:g:", + long_options, &option_index)) != -1) { + switch (c) { +@@ -1169,6 +1204,9 @@ main(int argc, char **argv) + case 'U': + mode = UDP_ONLY; + break; ++ case 'T': ++ tcp_tproxy = 1; ++ break; + case 'v': + verbose = 1; + break; +@@ -1255,6 +1293,9 @@ main(int argc, char **argv) + if (mode == TCP_ONLY) { + mode = conf->mode; + } ++ if (tcp_tproxy == 0) { ++ tcp_tproxy = conf->tcp_tproxy; ++ } + if (mtu == 0) { + mtu = conf->mtu; + } +--- a/src/utils.c ++++ b/src/utils.c +@@ -342,6 +342,10 @@ usage() + #endif + printf( + " [-U] Enable UDP relay and disable TCP relay.\n"); ++#ifdef MODULE_REDIR ++ printf( ++ " [-T] Use tproxy instead of redirect (for tcp).\n"); ++#endif + #ifdef MODULE_REMOTE + printf( + " [-6] Resovle hostname to IPv6 address first.\n"); diff --git a/shadowsocksr-libev/patches/104-pointer-used-after-free.patch b/shadowsocksr-libev/patches/104-pointer-used-after-free.patch new file mode 100644 index 000000000..9081d5560 --- /dev/null +++ b/shadowsocksr-libev/patches/104-pointer-used-after-free.patch @@ -0,0 +1,16 @@ +--- a/server/server.c ++++ b/server/server.c +@@ -1942,12 +1942,12 @@ + text = (char*)malloc(strlen(protocol) - 11); + memcpy(text, protocol, strlen(protocol) - 11); + int length = strlen(protocol) - 11; +- free(protocol); + obfs = (char*)malloc(length); + memset(protocol, 0x00, length); + memcpy(protocol, text, length); + LOGI("protocol compatible enable, %s", protocol); + free(text); ++ free(protocol); + protocol_compatible = 1; + } + } diff --git a/shadowsocksr-libev/patches/105-Upgrade-PCRE-to-PCRE2.patch b/shadowsocksr-libev/patches/105-Upgrade-PCRE-to-PCRE2.patch new file mode 100644 index 000000000..1e493bd28 --- /dev/null +++ b/shadowsocksr-libev/patches/105-Upgrade-PCRE-to-PCRE2.patch @@ -0,0 +1,494 @@ +From 32f944b9a06fb2be4cd50da2434f2fd4b4decede Mon Sep 17 00:00:00 2001 +From: sbwml <984419930@qq.com> +Date: Thu, 1 Feb 2024 21:21:56 +0800 +Subject: [PATCH] Upgrade PCRE to PCRE2 + +Signed-off-by: sbwml <984419930@qq.com> +--- + configure.ac | 8 +-- + m4/pcre.m4 | 152 ------------------------------------------ + m4/pcre2.m4 | 181 +++++++++++++++++++++++++++++++++++++++++++++++++++ + src/rule.c | 47 ++++++++++--- + src/rule.h | 22 +++++-- + 5 files changed, 238 insertions(+), 172 deletions(-) + delete mode 100644 m4/pcre.m4 + create mode 100644 m4/pcre2.m4 + +diff --git a/configure.ac b/configure.ac +index f9c51ab..0513db0 100755 +--- a/configure.ac ++++ b/configure.ac +@@ -20,10 +20,10 @@ AC_DISABLE_STATIC + AC_DISABLE_SHARED + LT_INIT([dlopen]) + +-dnl Check for pcre library +-TS_CHECK_PCRE +-if test "x${enable_pcre}" != "xyes"; then +- AC_MSG_ERROR([Cannot find pcre library. Configure --with-pcre=DIR]) ++dnl Check for pcre2 library ++TS_CHECK_PCRE2 ++if test "x${enable_pcre2}" != "xyes"; then ++ AC_MSG_ERROR([Cannot find pcre2 library. Configure --with-pcre2=DIR]) + fi + + dnl Checks for using shared libraries from system +diff --git a/m4/pcre.m4 b/m4/pcre.m4 +deleted file mode 100644 +index 4d965bc..0000000 +--- a/m4/pcre.m4 ++++ /dev/null +@@ -1,152 +0,0 @@ +-dnl -------------------------------------------------------- -*- autoconf -*- +-dnl Licensed to the Apache Software Foundation (ASF) under one or more +-dnl contributor license agreements. See the NOTICE file distributed with +-dnl this work for additional information regarding copyright ownership. +-dnl The ASF licenses this file to You under the Apache License, Version 2.0 +-dnl (the "License"); you may not use this file except in compliance with +-dnl the License. You may obtain a copy of the License at +-dnl +-dnl http://www.apache.org/licenses/LICENSE-2.0 +-dnl +-dnl Unless required by applicable law or agreed to in writing, software +-dnl distributed under the License is distributed on an "AS IS" BASIS, +-dnl WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-dnl See the License for the specific language governing permissions and +-dnl limitations under the License. +- +-dnl +-dnl TS_ADDTO(variable, value) +-dnl +-dnl Add value to variable +-dnl +-AC_DEFUN([TS_ADDTO], [ +- if test "x$$1" = "x"; then +- test "x$verbose" = "xyes" && echo " setting $1 to \"$2\"" +- $1="$2" +- else +- ats_addto_bugger="$2" +- for i in $ats_addto_bugger; do +- ats_addto_duplicate="0" +- for j in $$1; do +- if test "x$i" = "x$j"; then +- ats_addto_duplicate="1" +- break +- fi +- done +- if test $ats_addto_duplicate = "0"; then +- test "x$verbose" = "xyes" && echo " adding \"$i\" to $1" +- $1="$$1 $i" +- fi +- done +- fi +-])dnl +- +-dnl +-dnl TS_ADDTO_RPATH(path) +-dnl +-dnl Adds path to variable with the '-rpath' directive. +-dnl +-AC_DEFUN([TS_ADDTO_RPATH], [ +- AC_MSG_NOTICE([adding $1 to RPATH]) +- TS_ADDTO(LIBTOOL_LINK_FLAGS, [-R$1]) +-])dnl +- +-dnl +-dnl pcre.m4: Trafficserver's pcre autoconf macros +-dnl +- +-dnl +-dnl TS_CHECK_PCRE: look for pcre libraries and headers +-dnl +-AC_DEFUN([TS_CHECK_PCRE], [ +-enable_pcre=no +-AC_ARG_WITH(pcre, [AC_HELP_STRING([--with-pcre=DIR],[use a specific pcre library])], +-[ +- if test "x$withval" != "xyes" && test "x$withval" != "x"; then +- pcre_base_dir="$withval" +- if test "$withval" != "no"; then +- enable_pcre=yes +- case "$withval" in +- *":"*) +- pcre_include="`echo $withval |sed -e 's/:.*$//'`" +- pcre_ldflags="`echo $withval |sed -e 's/^.*://'`" +- AC_MSG_CHECKING(checking for pcre includes in $pcre_include libs in $pcre_ldflags ) +- ;; +- *) +- pcre_include="$withval/include" +- pcre_ldflags="$withval/lib" +- AC_MSG_CHECKING(checking for pcre includes in $withval) +- ;; +- esac +- fi +- fi +-], +-[ +- AC_CHECK_PROG(PCRE_CONFIG, pcre-config, pcre-config) +- if test "x$PCRE_CONFIG" != "x"; then +- enable_pcre=yes +- pcre_base_dir="`$PCRE_CONFIG --prefix`" +- pcre_include="`$PCRE_CONFIG --cflags | sed -es/-I//`" +- pcre_ldflags="`$PCRE_CONFIG --libs | sed -es/-lpcre// -es/-L//`" +- fi +-]) +- +-if test "x$pcre_base_dir" = "x"; then +- AC_MSG_CHECKING([for pcre location]) +- AC_CACHE_VAL(ats_cv_pcre_dir,[ +- for dir in /usr/local /usr ; do +- if test -d $dir && ( test -f $dir/include/pcre.h || test -f $dir/include/pcre/pcre.h ); then +- ats_cv_pcre_dir=$dir +- break +- fi +- done +- ]) +- pcre_base_dir=$ats_cv_pcre_dir +- if test "x$pcre_base_dir" = "x"; then +- enable_pcre=no +- AC_MSG_RESULT([not found]) +- else +- enable_pcre=yes +- pcre_include="$pcre_base_dir/include" +- pcre_ldflags="$pcre_base_dir/lib" +- AC_MSG_RESULT([$pcre_base_dir]) +- fi +-else +- AC_MSG_CHECKING(for pcre headers in $pcre_include) +- if test -d $pcre_include && test -d $pcre_ldflags && ( test -f $pcre_include/pcre.h || test -f $pcre_include/pcre/pcre.h ); then +- AC_MSG_RESULT([ok]) +- else +- AC_MSG_RESULT([not found]) +- fi +-fi +- +-pcreh=0 +-pcre_pcreh=0 +-if test "$enable_pcre" != "no"; then +- saved_ldflags=$LDFLAGS +- saved_cppflags=$CFLAGS +- pcre_have_headers=0 +- pcre_have_libs=0 +- if test "$pcre_base_dir" != "/usr"; then +- TS_ADDTO(CFLAGS, [-I${pcre_include}]) +- TS_ADDTO(CFLAGS, [-DPCRE_STATIC]) +- TS_ADDTO(LDFLAGS, [-L${pcre_ldflags}]) +- TS_ADDTO_RPATH(${pcre_ldflags}) +- fi +- AC_SEARCH_LIBS([pcre_exec], [pcre], [pcre_have_libs=1]) +- if test "$pcre_have_libs" != "0"; then +- AC_CHECK_HEADERS(pcre.h, [pcre_have_headers=1]) +- AC_CHECK_HEADERS(pcre/pcre.h, [pcre_have_headers=1]) +- fi +- if test "$pcre_have_headers" != "0"; then +- AC_DEFINE(HAVE_LIBPCRE,1,[Compiling with pcre support]) +- AC_SUBST(LIBPCRE, [-lpcre]) +- else +- enable_pcre=no +- CFLAGS=$saved_cppflags +- LDFLAGS=$saved_ldflags +- fi +-fi +-AC_SUBST(pcreh) +-AC_SUBST(pcre_pcreh) +-]) +diff --git a/m4/pcre2.m4 b/m4/pcre2.m4 +new file mode 100644 +index 0000000..fcec4b7 +--- /dev/null ++++ b/m4/pcre2.m4 +@@ -0,0 +1,181 @@ ++dnl -------------------------------------------------------- -*- autoconf -*- ++dnl Licensed to the Apache Software Foundation (ASF) under one or more ++dnl contributor license agreements. See the NOTICE file distributed with ++dnl this work for additional information regarding copyright ownership. ++dnl The ASF licenses this file to You under the Apache License, Version 2.0 ++dnl (the "License"); you may not use this file except in compliance with ++dnl the License. You may obtain a copy of the License at ++dnl ++dnl http://www.apache.org/licenses/LICENSE-2.0 ++dnl ++dnl Unless required by applicable law or agreed to in writing, software ++dnl distributed under the License is distributed on an "AS IS" BASIS, ++dnl WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ++dnl See the License for the specific language governing permissions and ++dnl limitations under the License. ++ ++dnl Modified by Syrone Wong to support pcre2 8bit variant only ++ ++dnl ++dnl TS_ADDTO(variable, value) ++dnl ++dnl Add value to variable ++dnl ++AC_DEFUN([TS_ADDTO], [ ++ if test "x$$1" = "x"; then ++ test "x$verbose" = "xyes" && echo " setting $1 to \"$2\"" ++ $1="$2" ++ else ++ ats_addto_bugger="$2" ++ for i in $ats_addto_bugger; do ++ ats_addto_duplicate="0" ++ for j in $$1; do ++ if test "x$i" = "x$j"; then ++ ats_addto_duplicate="1" ++ break ++ fi ++ done ++ if test $ats_addto_duplicate = "0"; then ++ test "x$verbose" = "xyes" && echo " adding \"$i\" to $1" ++ $1="$$1 $i" ++ fi ++ done ++ fi ++])dnl ++ ++dnl ++dnl TS_ADDTO_RPATH(path) ++dnl ++dnl Adds path to variable with the '-rpath' directive. ++dnl ++AC_DEFUN([TS_ADDTO_RPATH], [ ++ AC_MSG_NOTICE([adding $1 to RPATH]) ++ TS_ADDTO(LIBTOOL_LINK_FLAGS, [-R$1]) ++])dnl ++ ++dnl ++dnl pcre2.m4: Trafficserver's pcre2 autoconf macros ++dnl ++ ++dnl ++dnl TS_CHECK_PCRE2: look for pcre2 libraries and headers ++dnl ++AC_DEFUN([TS_CHECK_PCRE2], [ ++enable_pcre2=no ++AC_ARG_WITH(pcre2, [AC_HELP_STRING([--with-pcre2=DIR],[use a specific pcre2 library])], ++[ ++ if test "x$withval" != "xyes" && test "x$withval" != "x"; then ++ pcre2_base_dir="$withval" ++ if test "$withval" != "no"; then ++ enable_pcre2=yes ++ case "$withval" in ++ *":"*) ++ pcre2_include="`echo $withval |sed -e 's/:.*$//'`" ++ pcre2_ldflags="`echo $withval |sed -e 's/^.*://'`" ++ AC_MSG_CHECKING(checking for pcre2 includes in $pcre2_include libs in $pcre2_ldflags ) ++ ;; ++ *) ++ pcre2_include="$withval/include" ++ pcre2_ldflags="$withval/lib" ++ AC_MSG_CHECKING(checking for pcre2 includes in $withval) ++ ;; ++ esac ++ fi ++ fi ++], ++[ ++ AC_CHECK_PROG(PCRE2_CONFIG, pcre2-config, pcre2-config) ++ if test "x$PCRE2_CONFIG" != "x"; then ++ enable_pcre2=yes ++ pcre2_base_dir="`$PCRE2_CONFIG --prefix`" ++ pcre2_include="`$PCRE2_CONFIG --cflags | sed -es/-I//`" ++ pcre2_ldflags="`$PCRE2_CONFIG --libs8 | sed -es/-lpcre2-8// -es/-L//`" ++ fi ++]) ++ ++if test "x$pcre2_base_dir" = "x"; then ++ AC_MSG_CHECKING([for pcre2 location]) ++ AC_CACHE_VAL(ats_cv_pcre2_dir,[ ++ for dir in /usr/local /usr ; do ++ if test -d $dir && ( test -f $dir/include/pcre2.h || test -f $dir/include/pcre2/pcre2.h ); then ++ ats_cv_pcre2_dir=$dir ++ break ++ fi ++ done ++ ]) ++ pcre2_base_dir=$ats_cv_pcre2_dir ++ if test "x$pcre2_base_dir" = "x"; then ++ enable_pcre2=no ++ AC_MSG_RESULT([not found]) ++ else ++ enable_pcre2=yes ++ pcre2_include="$pcre2_base_dir/include" ++ pcre2_ldflags="$pcre2_base_dir/lib" ++ AC_MSG_RESULT([$pcre2_base_dir]) ++ fi ++else ++ AC_MSG_CHECKING(for pcre2 headers in $pcre2_include) ++ if test -d $pcre2_include && test -d $pcre2_ldflags && ( test -f $pcre2_include/pcre2.h || test -f $pcre2_include/pcre2/pcre2.h ); then ++ AC_MSG_RESULT([ok]) ++ else ++ AC_MSG_RESULT([not found]) ++ fi ++fi ++ ++pcre2h=0 ++pcre2_pcre2h=0 ++if test "$enable_pcre2" != "no"; then ++ saved_ldflags=$LDFLAGS ++ saved_cppflags=$CFLAGS ++ pcre2_have_headers=0 ++ pcre2_have_libs=0 ++ if test "$pcre2_base_dir" != "/usr"; then ++ TS_ADDTO(CFLAGS, [-I${pcre2_include}]) ++ TS_ADDTO(CFLAGS, [-DPCRE2_STATIC]) ++ TS_ADDTO(LDFLAGS, [-L${pcre2_ldflags}]) ++ TS_ADDTO_RPATH(${pcre2_ldflags}) ++ fi ++ AC_SEARCH_LIBS([pcre2_match_8], [pcre2-8], [pcre2_have_libs=1]) ++ if test "$pcre2_have_libs" != "0"; then ++ AC_MSG_CHECKING([pcre2.h]) ++ AC_COMPILE_IFELSE( ++ [AC_LANG_PROGRAM( ++ [[ ++#define PCRE2_CODE_UNIT_WIDTH 8 ++#include ++ ]], ++ [[ ++ ]] ++ )], ++ [pcre2_have_headers=1 ++ AC_MSG_RESULT([ok])], ++ [AC_MSG_RESULT([not found])] ++ ) ++ ++ AC_MSG_CHECKING([pcre2/pcre2.h]) ++ AC_COMPILE_IFELSE( ++ [AC_LANG_PROGRAM( ++ [[ ++#define PCRE2_CODE_UNIT_WIDTH 8 ++#include ++ ]], ++ [[ ++ ]] ++ )], ++ [pcre2_have_headers=1 ++ AC_MSG_RESULT([ok])], ++ [AC_MSG_RESULT([not found])] ++ ) ++ fi ++ if test "$pcre2_have_headers" != "0"; then ++ AC_DEFINE(HAVE_LIBPCRE2,1,[Compiling with pcre2 support]) ++ AC_SUBST(LIBPCRE2, [-lpcre2-8]) ++ else ++ enable_pcre2=no ++ CFLAGS=$saved_cppflags ++ LDFLAGS=$saved_ldflags ++ fi ++fi ++AC_SUBST(pcre2h) ++AC_SUBST(pcre2_pcre2h) ++]) +diff --git a/src/rule.c b/src/rule.c +index 8aae04e..41ba5e7 100644 +--- a/src/rule.c ++++ b/src/rule.c +@@ -82,14 +82,28 @@ int + init_rule(rule_t *rule) + { + if (rule->pattern_re == NULL) { +- const char *reerr; +- int reerroffset; ++ int errornumber; ++ PCRE2_SIZE erroroffset; ++ rule->pattern_re = pcre2_compile( ++ (PCRE2_SPTR)rule->pattern, /* the pattern */ ++ PCRE2_ZERO_TERMINATED, /* indicates pattern is zero-terminated */ ++ 0, /* default options */ ++ &errornumber, /* for error number */ ++ &erroroffset, /* for error offset */ ++ NULL); /* use default compile context */ + +- rule->pattern_re = +- pcre_compile(rule->pattern, 0, &reerr, &reerroffset, NULL); + if (rule->pattern_re == NULL) { +- LOGE("Regex compilation of \"%s\" failed: %s, offset %d", +- rule->pattern, reerr, reerroffset); ++ PCRE2_UCHAR errbuffer[512]; ++ pcre2_get_error_message(errornumber, errbuffer, sizeof(errbuffer)); ++ LOGE("PCRE2 regex compilation failed at offset %d: %s\n", (int)erroroffset, ++ errbuffer); ++ return 0; ++ } ++ ++ rule->pattern_re_match_data = pcre2_match_data_create_from_pattern(rule->pattern_re, NULL); ++ ++ if (rule->pattern_re_match_data == NULL) { ++ ERROR("PCRE2: the memory for the block could not be obtained"); + return 0; + } + } +@@ -109,8 +123,15 @@ lookup_rule(const struct cork_dllist *rules, const char *name, size_t name_len) + + cork_dllist_foreach_void(rules, curr, next) { + rule_t *rule = cork_container_of(curr, rule_t, entries); +- if (pcre_exec(rule->pattern_re, NULL, +- name, name_len, 0, 0, NULL, 0) >= 0) ++ if (pcre2_match( ++ rule->pattern_re, /* the compiled pattern */ ++ (PCRE2_SPTR)name, /* the subject string */ ++ name_len, /* the length of the subject */ ++ 0, /* start at offset 0 in the subject */ ++ 0, /* default options */ ++ rule->pattern_re_match_data, /* block for storing the result */ ++ NULL /* use default match context */ ++ ) >= 0) + return rule; + } + +@@ -131,7 +152,13 @@ free_rule(rule_t *rule) + return; + + ss_free(rule->pattern); +- if (rule->pattern_re != NULL) +- pcre_free(rule->pattern_re); ++ if (rule->pattern_re != NULL) { ++ pcre2_code_free(rule->pattern_re); /* data and the compiled pattern. */ ++ rule->pattern_re = NULL; ++ } ++ if (rule->pattern_re_match_data != NULL) { ++ pcre2_match_data_free(rule->pattern_re_match_data); /* Release memory used for the match */ ++ rule->pattern_re_match_data = NULL; ++ } + ss_free(rule); + } +diff --git a/src/rule.h b/src/rule.h +index 015bc42..84a89af 100644 +--- a/src/rule.h ++++ b/src/rule.h +@@ -33,17 +33,27 @@ + + #include + +-#ifdef HAVE_PCRE_H +-#include +-#elif HAVE_PCRE_PCRE_H +-#include +-#endif ++/* ++ * The PCRE2_CODE_UNIT_WIDTH macro must be defined before including pcre2.h. ++ * For a program that uses only one code unit width, setting it to 8, 16, or 32 ++ * makes it possible to use generic function names such as pcre2_compile(). Note ++ * that just changing 8 to 16 (for example) is not sufficient to convert this ++ * program to process 16-bit characters. Even in a fully 16-bit environment, where ++ * string-handling functions such as strcmp() and printf() work with 16-bit ++ * characters, the code for handling the table of named substrings will still need ++ * to be modified. ++ */ ++/* we only need to support ASCII chartable, thus set it to 8 */ ++#define PCRE2_CODE_UNIT_WIDTH 8 ++ ++#include + + typedef struct rule { + char *pattern; + + /* Runtime fields */ +- pcre *pattern_re; ++ pcre2_code *pattern_re; ++ pcre2_match_data *pattern_re_match_data; + + struct cork_dllist_item entries; + } rule_t; +-- +2.42.0 + diff --git a/shadowsocksr-libev/src/server/Makefile.am b/shadowsocksr-libev/src/server/Makefile.am new file mode 100644 index 000000000..3ae8bc27e --- /dev/null +++ b/shadowsocksr-libev/src/server/Makefile.am @@ -0,0 +1,55 @@ +VERSION_INFO = 2:0:0 + +AM_CFLAGS = -g -O2 -Wall -Werror -Wno-deprecated-declarations -fno-strict-aliasing -std=gnu99 -D_GNU_SOURCE +AM_CFLAGS += $(PTHREAD_CFLAGS) +if !USE_SYSTEM_SHARED_LIB +AM_CFLAGS += -I$(top_srcdir)/libev +AM_CFLAGS += -I$(top_srcdir)/libudns +AM_CFLAGS += -I$(top_srcdir)/libsodium/src/libsodium/include +endif +AM_CFLAGS += -I$(top_srcdir)/libipset/include +AM_CFLAGS += -I$(top_srcdir)/libcork/include +AM_CFLAGS += $(LIBPCRE_CFLAGS) + +SS_COMMON_LIBS = $(top_builddir)/libipset/libipset.la \ + $(top_builddir)/libcork/libcork.la \ + $(INET_NTOP_LIB) $(LIBPCRE_LIBS) +if USE_SYSTEM_SHARED_LIB +SS_COMMON_LIBS += -lev -lsodium -lm +else +SS_COMMON_LIBS += $(top_builddir)/libev/libev.la \ + $(top_builddir)/libsodium/src/libsodium/libsodium.la +endif + +bin_PROGRAMS = ss-server ss-check + +sni_src = http.c \ + tls.c \ + rule.c + +ss_check_SOURCES = check.c + +ss_server_SOURCES = utils.c \ + netutils.c \ + jconf.c \ + json.c \ + encrypt.c \ + udprelay.c \ + cache.c \ + acl.c \ + resolv.c \ + server.c \ + $(sni_src) + + +ss_check_LDADD = $(SS_COMMON_LIBS) +ss_server_LDADD = $(SS_COMMON_LIBS) + +if USE_SYSTEM_SHARED_LIB +ss_server_LDADD += -ludns +else +ss_server_LDADD += $(top_builddir)/libudns/libudns.la +endif + +ss_check_CFLAGS = $(AM_CFLAGS) +ss_server_CFLAGS = $(AM_CFLAGS) -DMODULE_REMOTE diff --git a/shadowsocksr-libev/src/server/Makefile.in b/shadowsocksr-libev/src/server/Makefile.in new file mode 100644 index 000000000..17dad9597 --- /dev/null +++ b/shadowsocksr-libev/src/server/Makefile.in @@ -0,0 +1,919 @@ +# Makefile.in generated by automake 1.15 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2014 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +@USE_SYSTEM_SHARED_LIB_FALSE@am__append_1 = -I$(top_srcdir)/libev \ +@USE_SYSTEM_SHARED_LIB_FALSE@ -I$(top_srcdir)/libudns \ +@USE_SYSTEM_SHARED_LIB_FALSE@ -I$(top_srcdir)/libsodium/src/libsodium/include +@USE_SYSTEM_SHARED_LIB_TRUE@am__append_2 = -lev -lsodium -lm +@USE_SYSTEM_SHARED_LIB_FALSE@am__append_3 = $(top_builddir)/libev/libev.la \ +@USE_SYSTEM_SHARED_LIB_FALSE@ $(top_builddir)/libsodium/src/libsodium/libsodium.la + +bin_PROGRAMS = ss-server$(EXEEXT) ss-check$(EXEEXT) +@USE_SYSTEM_SHARED_LIB_TRUE@am__append_4 = -ludns +@USE_SYSTEM_SHARED_LIB_FALSE@am__append_5 = $(top_builddir)/libudns/libudns.la +subdir = server +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_pthread.m4 \ + $(top_srcdir)/m4/ax_tls.m4 $(top_srcdir)/m4/inet_ntop.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ + $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ + $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/mbedtls.m4 \ + $(top_srcdir)/m4/openssl.m4 $(top_srcdir)/m4/pcre2.m4 \ + $(top_srcdir)/m4/polarssl.m4 \ + $(top_srcdir)/m4/stack-protector.m4 $(top_srcdir)/m4/zlib.m4 \ + $(top_srcdir)/libev/libev.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__installdirs = "$(DESTDIR)$(bindir)" +PROGRAMS = $(bin_PROGRAMS) +am_ss_check_OBJECTS = ss_check-check.$(OBJEXT) +ss_check_OBJECTS = $(am_ss_check_OBJECTS) +am__DEPENDENCIES_1 = +am__DEPENDENCIES_2 = $(top_builddir)/libipset/libipset.la \ + $(top_builddir)/libcork/libcork.la $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(am__append_3) +ss_check_DEPENDENCIES = $(am__DEPENDENCIES_2) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +ss_check_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(ss_check_CFLAGS) \ + $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +am__objects_1 = ss_server-http.$(OBJEXT) ss_server-tls.$(OBJEXT) \ + ss_server-rule.$(OBJEXT) +am_ss_server_OBJECTS = ss_server-utils.$(OBJEXT) \ + ss_server-netutils.$(OBJEXT) ss_server-jconf.$(OBJEXT) \ + ss_server-json.$(OBJEXT) ss_server-encrypt.$(OBJEXT) \ + ss_server-udprelay.$(OBJEXT) ss_server-cache.$(OBJEXT) \ + ss_server-acl.$(OBJEXT) ss_server-resolv.$(OBJEXT) \ + ss_server-server.$(OBJEXT) $(am__objects_1) +ss_server_OBJECTS = $(am_ss_server_OBJECTS) +ss_server_DEPENDENCIES = $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_1) \ + $(am__append_5) +ss_server_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(ss_server_CFLAGS) \ + $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/auto/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(ss_check_SOURCES) $(ss_server_SOURCES) +DIST_SOURCES = $(ss_check_SOURCES) $(ss_server_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/auto/depcomp +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +ASCIIDOC = @ASCIIDOC@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GREP = @GREP@ +GZIP = @GZIP@ +INET_NTOP_LIB = @INET_NTOP_LIB@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBPCRE = @LIBPCRE@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +MV = @MV@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PCRE_CONFIG = @PCRE_CONFIG@ +PTHREAD_CC = @PTHREAD_CC@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ +PTHREAD_LIBS = @PTHREAD_LIBS@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +XMLTO = @XMLTO@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +ax_pthread_config = @ax_pthread_config@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pcre_pcreh = @pcre_pcreh@ +pcreh = @pcreh@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +subdirs = @subdirs@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +VERSION_INFO = 2:0:0 +AM_CFLAGS = -g -O2 -Wall -Werror -Wno-deprecated-declarations \ + -fno-strict-aliasing -std=gnu99 -D_GNU_SOURCE \ + $(PTHREAD_CFLAGS) $(am__append_1) \ + -I$(top_srcdir)/libipset/include \ + -I$(top_srcdir)/libcork/include $(LIBPCRE_CFLAGS) +SS_COMMON_LIBS = $(top_builddir)/libipset/libipset.la \ + $(top_builddir)/libcork/libcork.la $(INET_NTOP_LIB) \ + $(LIBPCRE_LIBS) $(am__append_2) $(am__append_3) +sni_src = http.c \ + tls.c \ + rule.c + +ss_check_SOURCES = check.c +ss_server_SOURCES = utils.c \ + netutils.c \ + jconf.c \ + json.c \ + encrypt.c \ + udprelay.c \ + cache.c \ + acl.c \ + resolv.c \ + server.c \ + $(sni_src) + +ss_check_LDADD = $(SS_COMMON_LIBS) +ss_server_LDADD = $(SS_COMMON_LIBS) $(am__append_4) $(am__append_5) +ss_check_CFLAGS = $(AM_CFLAGS) +ss_server_CFLAGS = $(AM_CFLAGS) -DMODULE_REMOTE +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign server/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign server/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +install-binPROGRAMS: $(bin_PROGRAMS) + @$(NORMAL_INSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \ + fi; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p \ + || test -f $$p1 \ + ; then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' \ + -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-binPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' \ + `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(bindir)" && rm -f $$files + +clean-binPROGRAMS: + @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list + +ss-check$(EXEEXT): $(ss_check_OBJECTS) $(ss_check_DEPENDENCIES) $(EXTRA_ss_check_DEPENDENCIES) + @rm -f ss-check$(EXEEXT) + $(AM_V_CCLD)$(ss_check_LINK) $(ss_check_OBJECTS) $(ss_check_LDADD) $(LIBS) + +ss-server$(EXEEXT): $(ss_server_OBJECTS) $(ss_server_DEPENDENCIES) $(EXTRA_ss_server_DEPENDENCIES) + @rm -f ss-server$(EXEEXT) + $(AM_V_CCLD)$(ss_server_LINK) $(ss_server_OBJECTS) $(ss_server_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ss_check-check.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ss_server-acl.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ss_server-cache.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ss_server-encrypt.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ss_server-http.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ss_server-jconf.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ss_server-json.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ss_server-netutils.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ss_server-resolv.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ss_server-rule.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ss_server-server.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ss_server-tls.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ss_server-udprelay.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ss_server-utils.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +ss_check-check.o: check.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_check_CFLAGS) $(CFLAGS) -MT ss_check-check.o -MD -MP -MF $(DEPDIR)/ss_check-check.Tpo -c -o ss_check-check.o `test -f 'check.c' || echo '$(srcdir)/'`check.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_check-check.Tpo $(DEPDIR)/ss_check-check.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='check.c' object='ss_check-check.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_check_CFLAGS) $(CFLAGS) -c -o ss_check-check.o `test -f 'check.c' || echo '$(srcdir)/'`check.c + +ss_check-check.obj: check.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_check_CFLAGS) $(CFLAGS) -MT ss_check-check.obj -MD -MP -MF $(DEPDIR)/ss_check-check.Tpo -c -o ss_check-check.obj `if test -f 'check.c'; then $(CYGPATH_W) 'check.c'; else $(CYGPATH_W) '$(srcdir)/check.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_check-check.Tpo $(DEPDIR)/ss_check-check.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='check.c' object='ss_check-check.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_check_CFLAGS) $(CFLAGS) -c -o ss_check-check.obj `if test -f 'check.c'; then $(CYGPATH_W) 'check.c'; else $(CYGPATH_W) '$(srcdir)/check.c'; fi` + +ss_server-utils.o: utils.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-utils.o -MD -MP -MF $(DEPDIR)/ss_server-utils.Tpo -c -o ss_server-utils.o `test -f 'utils.c' || echo '$(srcdir)/'`utils.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-utils.Tpo $(DEPDIR)/ss_server-utils.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils.c' object='ss_server-utils.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-utils.o `test -f 'utils.c' || echo '$(srcdir)/'`utils.c + +ss_server-utils.obj: utils.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-utils.obj -MD -MP -MF $(DEPDIR)/ss_server-utils.Tpo -c -o ss_server-utils.obj `if test -f 'utils.c'; then $(CYGPATH_W) 'utils.c'; else $(CYGPATH_W) '$(srcdir)/utils.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-utils.Tpo $(DEPDIR)/ss_server-utils.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils.c' object='ss_server-utils.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-utils.obj `if test -f 'utils.c'; then $(CYGPATH_W) 'utils.c'; else $(CYGPATH_W) '$(srcdir)/utils.c'; fi` + +ss_server-netutils.o: netutils.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-netutils.o -MD -MP -MF $(DEPDIR)/ss_server-netutils.Tpo -c -o ss_server-netutils.o `test -f 'netutils.c' || echo '$(srcdir)/'`netutils.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-netutils.Tpo $(DEPDIR)/ss_server-netutils.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='netutils.c' object='ss_server-netutils.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-netutils.o `test -f 'netutils.c' || echo '$(srcdir)/'`netutils.c + +ss_server-netutils.obj: netutils.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-netutils.obj -MD -MP -MF $(DEPDIR)/ss_server-netutils.Tpo -c -o ss_server-netutils.obj `if test -f 'netutils.c'; then $(CYGPATH_W) 'netutils.c'; else $(CYGPATH_W) '$(srcdir)/netutils.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-netutils.Tpo $(DEPDIR)/ss_server-netutils.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='netutils.c' object='ss_server-netutils.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-netutils.obj `if test -f 'netutils.c'; then $(CYGPATH_W) 'netutils.c'; else $(CYGPATH_W) '$(srcdir)/netutils.c'; fi` + +ss_server-jconf.o: jconf.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-jconf.o -MD -MP -MF $(DEPDIR)/ss_server-jconf.Tpo -c -o ss_server-jconf.o `test -f 'jconf.c' || echo '$(srcdir)/'`jconf.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-jconf.Tpo $(DEPDIR)/ss_server-jconf.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='jconf.c' object='ss_server-jconf.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-jconf.o `test -f 'jconf.c' || echo '$(srcdir)/'`jconf.c + +ss_server-jconf.obj: jconf.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-jconf.obj -MD -MP -MF $(DEPDIR)/ss_server-jconf.Tpo -c -o ss_server-jconf.obj `if test -f 'jconf.c'; then $(CYGPATH_W) 'jconf.c'; else $(CYGPATH_W) '$(srcdir)/jconf.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-jconf.Tpo $(DEPDIR)/ss_server-jconf.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='jconf.c' object='ss_server-jconf.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-jconf.obj `if test -f 'jconf.c'; then $(CYGPATH_W) 'jconf.c'; else $(CYGPATH_W) '$(srcdir)/jconf.c'; fi` + +ss_server-json.o: json.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-json.o -MD -MP -MF $(DEPDIR)/ss_server-json.Tpo -c -o ss_server-json.o `test -f 'json.c' || echo '$(srcdir)/'`json.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-json.Tpo $(DEPDIR)/ss_server-json.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='json.c' object='ss_server-json.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-json.o `test -f 'json.c' || echo '$(srcdir)/'`json.c + +ss_server-json.obj: json.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-json.obj -MD -MP -MF $(DEPDIR)/ss_server-json.Tpo -c -o ss_server-json.obj `if test -f 'json.c'; then $(CYGPATH_W) 'json.c'; else $(CYGPATH_W) '$(srcdir)/json.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-json.Tpo $(DEPDIR)/ss_server-json.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='json.c' object='ss_server-json.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-json.obj `if test -f 'json.c'; then $(CYGPATH_W) 'json.c'; else $(CYGPATH_W) '$(srcdir)/json.c'; fi` + +ss_server-encrypt.o: encrypt.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-encrypt.o -MD -MP -MF $(DEPDIR)/ss_server-encrypt.Tpo -c -o ss_server-encrypt.o `test -f 'encrypt.c' || echo '$(srcdir)/'`encrypt.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-encrypt.Tpo $(DEPDIR)/ss_server-encrypt.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='encrypt.c' object='ss_server-encrypt.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-encrypt.o `test -f 'encrypt.c' || echo '$(srcdir)/'`encrypt.c + +ss_server-encrypt.obj: encrypt.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-encrypt.obj -MD -MP -MF $(DEPDIR)/ss_server-encrypt.Tpo -c -o ss_server-encrypt.obj `if test -f 'encrypt.c'; then $(CYGPATH_W) 'encrypt.c'; else $(CYGPATH_W) '$(srcdir)/encrypt.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-encrypt.Tpo $(DEPDIR)/ss_server-encrypt.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='encrypt.c' object='ss_server-encrypt.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-encrypt.obj `if test -f 'encrypt.c'; then $(CYGPATH_W) 'encrypt.c'; else $(CYGPATH_W) '$(srcdir)/encrypt.c'; fi` + +ss_server-udprelay.o: udprelay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-udprelay.o -MD -MP -MF $(DEPDIR)/ss_server-udprelay.Tpo -c -o ss_server-udprelay.o `test -f 'udprelay.c' || echo '$(srcdir)/'`udprelay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-udprelay.Tpo $(DEPDIR)/ss_server-udprelay.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='udprelay.c' object='ss_server-udprelay.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-udprelay.o `test -f 'udprelay.c' || echo '$(srcdir)/'`udprelay.c + +ss_server-udprelay.obj: udprelay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-udprelay.obj -MD -MP -MF $(DEPDIR)/ss_server-udprelay.Tpo -c -o ss_server-udprelay.obj `if test -f 'udprelay.c'; then $(CYGPATH_W) 'udprelay.c'; else $(CYGPATH_W) '$(srcdir)/udprelay.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-udprelay.Tpo $(DEPDIR)/ss_server-udprelay.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='udprelay.c' object='ss_server-udprelay.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-udprelay.obj `if test -f 'udprelay.c'; then $(CYGPATH_W) 'udprelay.c'; else $(CYGPATH_W) '$(srcdir)/udprelay.c'; fi` + +ss_server-cache.o: cache.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-cache.o -MD -MP -MF $(DEPDIR)/ss_server-cache.Tpo -c -o ss_server-cache.o `test -f 'cache.c' || echo '$(srcdir)/'`cache.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-cache.Tpo $(DEPDIR)/ss_server-cache.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cache.c' object='ss_server-cache.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-cache.o `test -f 'cache.c' || echo '$(srcdir)/'`cache.c + +ss_server-cache.obj: cache.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-cache.obj -MD -MP -MF $(DEPDIR)/ss_server-cache.Tpo -c -o ss_server-cache.obj `if test -f 'cache.c'; then $(CYGPATH_W) 'cache.c'; else $(CYGPATH_W) '$(srcdir)/cache.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-cache.Tpo $(DEPDIR)/ss_server-cache.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cache.c' object='ss_server-cache.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-cache.obj `if test -f 'cache.c'; then $(CYGPATH_W) 'cache.c'; else $(CYGPATH_W) '$(srcdir)/cache.c'; fi` + +ss_server-acl.o: acl.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-acl.o -MD -MP -MF $(DEPDIR)/ss_server-acl.Tpo -c -o ss_server-acl.o `test -f 'acl.c' || echo '$(srcdir)/'`acl.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-acl.Tpo $(DEPDIR)/ss_server-acl.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='acl.c' object='ss_server-acl.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-acl.o `test -f 'acl.c' || echo '$(srcdir)/'`acl.c + +ss_server-acl.obj: acl.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-acl.obj -MD -MP -MF $(DEPDIR)/ss_server-acl.Tpo -c -o ss_server-acl.obj `if test -f 'acl.c'; then $(CYGPATH_W) 'acl.c'; else $(CYGPATH_W) '$(srcdir)/acl.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-acl.Tpo $(DEPDIR)/ss_server-acl.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='acl.c' object='ss_server-acl.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-acl.obj `if test -f 'acl.c'; then $(CYGPATH_W) 'acl.c'; else $(CYGPATH_W) '$(srcdir)/acl.c'; fi` + +ss_server-resolv.o: resolv.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-resolv.o -MD -MP -MF $(DEPDIR)/ss_server-resolv.Tpo -c -o ss_server-resolv.o `test -f 'resolv.c' || echo '$(srcdir)/'`resolv.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-resolv.Tpo $(DEPDIR)/ss_server-resolv.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='resolv.c' object='ss_server-resolv.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-resolv.o `test -f 'resolv.c' || echo '$(srcdir)/'`resolv.c + +ss_server-resolv.obj: resolv.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-resolv.obj -MD -MP -MF $(DEPDIR)/ss_server-resolv.Tpo -c -o ss_server-resolv.obj `if test -f 'resolv.c'; then $(CYGPATH_W) 'resolv.c'; else $(CYGPATH_W) '$(srcdir)/resolv.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-resolv.Tpo $(DEPDIR)/ss_server-resolv.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='resolv.c' object='ss_server-resolv.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-resolv.obj `if test -f 'resolv.c'; then $(CYGPATH_W) 'resolv.c'; else $(CYGPATH_W) '$(srcdir)/resolv.c'; fi` + +ss_server-server.o: server.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-server.o -MD -MP -MF $(DEPDIR)/ss_server-server.Tpo -c -o ss_server-server.o `test -f 'server.c' || echo '$(srcdir)/'`server.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-server.Tpo $(DEPDIR)/ss_server-server.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='server.c' object='ss_server-server.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-server.o `test -f 'server.c' || echo '$(srcdir)/'`server.c + +ss_server-server.obj: server.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-server.obj -MD -MP -MF $(DEPDIR)/ss_server-server.Tpo -c -o ss_server-server.obj `if test -f 'server.c'; then $(CYGPATH_W) 'server.c'; else $(CYGPATH_W) '$(srcdir)/server.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-server.Tpo $(DEPDIR)/ss_server-server.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='server.c' object='ss_server-server.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-server.obj `if test -f 'server.c'; then $(CYGPATH_W) 'server.c'; else $(CYGPATH_W) '$(srcdir)/server.c'; fi` + +ss_server-http.o: http.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-http.o -MD -MP -MF $(DEPDIR)/ss_server-http.Tpo -c -o ss_server-http.o `test -f 'http.c' || echo '$(srcdir)/'`http.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-http.Tpo $(DEPDIR)/ss_server-http.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http.c' object='ss_server-http.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-http.o `test -f 'http.c' || echo '$(srcdir)/'`http.c + +ss_server-http.obj: http.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-http.obj -MD -MP -MF $(DEPDIR)/ss_server-http.Tpo -c -o ss_server-http.obj `if test -f 'http.c'; then $(CYGPATH_W) 'http.c'; else $(CYGPATH_W) '$(srcdir)/http.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-http.Tpo $(DEPDIR)/ss_server-http.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http.c' object='ss_server-http.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-http.obj `if test -f 'http.c'; then $(CYGPATH_W) 'http.c'; else $(CYGPATH_W) '$(srcdir)/http.c'; fi` + +ss_server-tls.o: tls.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-tls.o -MD -MP -MF $(DEPDIR)/ss_server-tls.Tpo -c -o ss_server-tls.o `test -f 'tls.c' || echo '$(srcdir)/'`tls.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-tls.Tpo $(DEPDIR)/ss_server-tls.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tls.c' object='ss_server-tls.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-tls.o `test -f 'tls.c' || echo '$(srcdir)/'`tls.c + +ss_server-tls.obj: tls.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-tls.obj -MD -MP -MF $(DEPDIR)/ss_server-tls.Tpo -c -o ss_server-tls.obj `if test -f 'tls.c'; then $(CYGPATH_W) 'tls.c'; else $(CYGPATH_W) '$(srcdir)/tls.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-tls.Tpo $(DEPDIR)/ss_server-tls.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tls.c' object='ss_server-tls.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-tls.obj `if test -f 'tls.c'; then $(CYGPATH_W) 'tls.c'; else $(CYGPATH_W) '$(srcdir)/tls.c'; fi` + +ss_server-rule.o: rule.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-rule.o -MD -MP -MF $(DEPDIR)/ss_server-rule.Tpo -c -o ss_server-rule.o `test -f 'rule.c' || echo '$(srcdir)/'`rule.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-rule.Tpo $(DEPDIR)/ss_server-rule.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rule.c' object='ss_server-rule.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-rule.o `test -f 'rule.c' || echo '$(srcdir)/'`rule.c + +ss_server-rule.obj: rule.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -MT ss_server-rule.obj -MD -MP -MF $(DEPDIR)/ss_server-rule.Tpo -c -o ss_server-rule.obj `if test -f 'rule.c'; then $(CYGPATH_W) 'rule.c'; else $(CYGPATH_W) '$(srcdir)/rule.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/ss_server-rule.Tpo $(DEPDIR)/ss_server-rule.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rule.c' object='ss_server-rule.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ss_server_CFLAGS) $(CFLAGS) -c -o ss_server-rule.obj `if test -f 'rule.c'; then $(CYGPATH_W) 'rule.c'; else $(CYGPATH_W) '$(srcdir)/rule.c'; fi` + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(PROGRAMS) +installdirs: + for dir in "$(DESTDIR)$(bindir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-binPROGRAMS clean-generic clean-libtool mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-binPROGRAMS + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-binPROGRAMS + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean \ + clean-binPROGRAMS clean-generic clean-libtool cscopelist-am \ + ctags ctags-am distclean distclean-compile distclean-generic \ + distclean-libtool distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-binPROGRAMS \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-man install-pdf \ + install-pdf-am install-ps install-ps-am install-strip \ + installcheck installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags tags-am uninstall uninstall-am uninstall-binPROGRAMS + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/shadowsocksr-libev/src/server/README.md b/shadowsocksr-libev/src/server/README.md new file mode 100644 index 000000000..ef6a20e39 --- /dev/null +++ b/shadowsocksr-libev/src/server/README.md @@ -0,0 +1,3 @@ +# server + +`ss-server` and `ss-check` from https://github.com/ywb94/shadowsocks-libev diff --git a/shadowsocksr-libev/src/server/acl.c b/shadowsocksr-libev/src/server/acl.c new file mode 100644 index 000000000..60d4b7274 --- /dev/null +++ b/shadowsocksr-libev/src/server/acl.c @@ -0,0 +1,597 @@ +/* + * acl.c - Manage the ACL (Access Control List) + * + * Copyright (C) 2013 - 2016, Max Lv + * + * This file is part of the shadowsocks-libev. + * + * shadowsocks-libev is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * shadowsocks-libev 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with shadowsocks-libev; see the file COPYING. If not, see + * . + */ + +#include +#include + +#include "rule.h" +#include "utils.h" +#include "cache.h" +#include "acl.h" + +static struct ip_set white_list_ipv4; +static struct ip_set white_list_ipv6; + +static struct ip_set black_list_ipv4; +static struct ip_set black_list_ipv6; + +static struct cork_dllist black_list_rules; +static struct cork_dllist white_list_rules; + +static int acl_mode = BLACK_LIST; + +static struct cache *block_list; + +static struct ip_set outbound_block_list_ipv4; +static struct ip_set outbound_block_list_ipv6; +static struct cork_dllist outbound_block_list_rules; + +#ifdef __linux__ + +#include +#include + +#define NO_FIREWALL_MODE 0 +#define IPTABLES_MODE 1 +#define FIREWALLD_MODE 2 + +static FILE *shell_stdin; +static int mode = NO_FIREWALL_MODE; + +static char chain_name[64]; +static char *iptables_init_chain = + "iptables -N %s; iptables -F %s; iptables -A OUTPUT -p tcp --tcp-flags RST RST -j %s"; +static char *iptables_remove_chain = + "iptables -D OUTPUT -p tcp --tcp-flags RST RST -j %s; iptables -F %s; iptables -X %s"; +static char *iptables_add_rule = "iptables -A %s -d %s -j DROP"; +static char *iptables_remove_rule = "iptables -D %s -d %s -j DROP"; + +static char *ip6tables_init_chain = + "ip6tables -N %s; ip6tables -F %s; ip6tables -A OUTPUT -p tcp --tcp-flags RST RST -j %s"; +static char *ip6tables_remove_chain = + "ip6tables -D OUTPUT -p tcp --tcp-flags RST RST -j %s; ip6tables -F %s; ip6tables -X %s"; +static char *ip6tables_add_rule = "ip6tables -A %s -d %s -j DROP"; +static char *ip6tables_remove_rule = "ip6tables -D %s -d %s -j DROP"; + +static char *firewalld_init_chain = + "firewall-cmd --direct --add-chain ipv4 filter %s; \ + firewall-cmd --direct --passthrough ipv4 -F %s; \ + firewall-cmd --direct --passthrough ipv4 -A OUTPUT -p tcp --tcp-flags RST RST -j %s"; +static char *firewalld_remove_chain = + "firewall-cmd --direct --passthrough ipv4 -D OUTPUT -p tcp --tcp-flags RST RST -j %s; \ + firewall-cmd --direct --passthrough ipv4 -F %s; \ + firewall-cmd --direct --remove-chain ipv4 filter %s"; +static char *firewalld_add_rule = "firewall-cmd --direct --passthrough ipv4 -A %s -d %s -j DROP"; +static char *firewalld_remove_rule = "firewall-cmd --direct --passthrough ipv4 -D %s -d %s -j DROP"; + +static char *firewalld6_init_chain = + "firewall-cmd --direct --add-chain ipv6 filter %s; \ + firewall-cmd --direct --passthrough ipv6 -F %s; \ + firewall-cmd --direct --passthrough ipv6 -A OUTPUT -p tcp --tcp-flags RST RST -j %s"; +static char *firewalld6_remove_chain = + "firewall-cmd --direct --passthrough ipv6 -D OUTPUT -p tcp --tcp-flags RST RST -j %s; \ + firewall-cmd --direct --passthrough ipv6 -F %s; \ + firewall-cmd --direct --remove-chain ipv6 filter %s"; +static char *firewalld6_add_rule = "firewall-cmd --direct --passthrough ipv6 -A %s -d %s -j DROP"; +static char *firewalld6_remove_rule = "firewall-cmd --direct --passthrough ipv6 -D %s -d %s -j DROP"; + +static int +run_cmd(const char *cmd) +{ + int ret = 0; + char cmdstring[256]; + + sprintf(cmdstring, "%s\n", cmd); + size_t len = strlen(cmdstring); + + if (shell_stdin != NULL) { + ret = fwrite(cmdstring, 1, len, shell_stdin); + fflush(shell_stdin); + } + + return ret == len; +} + +static int +init_firewall() +{ + int ret = 0; + char cli[256]; + FILE *fp; + + if (getuid() != 0) + return -1; + + sprintf(cli, "firewall-cmd --version 2>&1"); + fp = popen(cli, "r"); + + if (fp == NULL) + return -1; + + if (pclose(fp) == 0) { + mode = FIREWALLD_MODE; + } else { + /* Check whether we have permission to operate iptables. + * Note that checking `iptables --version` is insufficient: + * eg, running within a child user namespace. + */ + sprintf(cli, "iptables -L 2>&1"); + fp = popen(cli, "r"); + if (fp == NULL) + return -1; + if (pclose(fp) == 0) + mode = IPTABLES_MODE; + } + + sprintf(chain_name, "SHADOWSOCKS_LIBEV_%d", getpid()); + + if (mode == FIREWALLD_MODE) { + sprintf(cli, firewalld6_init_chain, chain_name, chain_name, chain_name); + ret |= system(cli); + sprintf(cli, firewalld_init_chain, chain_name, chain_name, chain_name); + ret |= system(cli); + } else if (mode == IPTABLES_MODE) { + sprintf(cli, ip6tables_init_chain, chain_name, chain_name, chain_name); + ret |= system(cli); + sprintf(cli, iptables_init_chain, chain_name, chain_name, chain_name); + ret |= system(cli); + } + + shell_stdin = popen("/bin/sh", "w"); + + return ret; +} + +static int +reset_firewall() +{ + int ret = 0; + char cli[256]; + + if (getuid() != 0) + return -1; + + if (mode == IPTABLES_MODE) { + sprintf(cli, ip6tables_remove_chain, chain_name, chain_name, chain_name); + ret |= system(cli); + sprintf(cli, iptables_remove_chain, chain_name, chain_name, chain_name); + ret |= system(cli); + } else if (mode == FIREWALLD_MODE) { + sprintf(cli, firewalld6_remove_chain, chain_name, chain_name, chain_name); + ret |= system(cli); + sprintf(cli, firewalld_remove_chain, chain_name, chain_name, chain_name); + ret |= system(cli); + } + + if (shell_stdin != NULL) { + run_cmd("exit 0"); + pclose(shell_stdin); + } + + return ret; +} + +static int +set_firewall_rule(char *addr, int add) +{ + char cli[256]; + struct cork_ip ip; + + if (getuid() != 0) + return -1; + + if (cork_ip_init(&ip, addr)) + return -1; + + if (add) { + if (mode == IPTABLES_MODE) + sprintf(cli, ip.version == 4 ? iptables_add_rule : ip6tables_add_rule, + chain_name, addr); + else if (mode == FIREWALLD_MODE) + sprintf(cli, ip.version == 4 ? firewalld_add_rule : firewalld6_add_rule, + chain_name, addr); + return run_cmd(cli); + } else { + if (mode == IPTABLES_MODE) + sprintf(cli, ip.version == 4 ? iptables_remove_rule : ip6tables_remove_rule, + chain_name, addr); + else if (mode == FIREWALLD_MODE) + sprintf(cli, ip.version == 4 ? firewalld_remove_rule : firewalld6_remove_rule, + chain_name, addr); + return run_cmd(cli); + } + + return 0; +} + +static void +free_firewall_rule(void *key, void *element) +{ + if (key == NULL) + return; + char *addr = (char *)key; + set_firewall_rule(addr, 0); + ss_free(element); +} + +#endif + +void +init_block_list(int firewall) +{ + // Initialize cache +#ifdef __linux__ + if (firewall) + init_firewall(); + else + mode = NO_FIREWALL_MODE; + cache_create(&block_list, 256, free_firewall_rule); +#else + cache_create(&block_list, 256, NULL); +#endif +} + +void +free_block_list() +{ +#ifdef __linux__ + if (mode != NO_FIREWALL_MODE) + reset_firewall(); +#endif + cache_clear(block_list, 0); // Remove all items +} + +int +remove_from_block_list(char *addr) +{ + size_t addr_len = strlen(addr); + return cache_remove(block_list, addr, addr_len); +} + +void +clear_block_list() +{ + cache_clear(block_list, 3600); // Clear items older than 1 hour +} + +int +check_block_list(char *addr) +{ + size_t addr_len = strlen(addr); + + if (cache_key_exist(block_list, addr, addr_len)) { + int *count = NULL; + cache_lookup(block_list, addr, addr_len, &count); + + if (count != NULL && *count > MAX_TRIES) + return 1; + } + + return 0; +} + +int +update_block_list(char *addr, int err_level) +{ + size_t addr_len = strlen(addr); + + if (cache_key_exist(block_list, addr, addr_len)) { + int *count = NULL; + cache_lookup(block_list, addr, addr_len, &count); + if (count != NULL) { + if (*count > MAX_TRIES) + return 1; + (*count) += err_level; + } + } else if (err_level > 0) { + int *count = (int *)ss_malloc(sizeof(int)); + *count = 1; + cache_insert(block_list, addr, addr_len, count); +#ifdef __linux__ + if (mode != NO_FIREWALL_MODE) + set_firewall_rule(addr, 1); +#endif + } + + return 0; +} + +static void +parse_addr_cidr(const char *str, char *host, int *cidr) +{ + int ret = -1, n = 0; + char *pch; + + pch = strchr(str, '/'); + while (pch != NULL) { + n++; + ret = pch - str; + pch = strchr(pch + 1, '/'); + } + if (ret == -1) { + strcpy(host, str); + *cidr = -1; + } else { + memcpy(host, str, ret); + host[ret] = '\0'; + *cidr = atoi(str + ret + 1); + } +} + +char * +trimwhitespace(char *str) +{ + char *end; + + // Trim leading space + while (isspace(*str)) + str++; + + if (*str == 0) // All spaces? + return str; + + // Trim trailing space + end = str + strlen(str) - 1; + while (end > str && isspace(*end)) + end--; + + // Write new null terminator + *(end + 1) = 0; + + return str; +} + +int +init_acl(const char *path) +{ + // initialize ipset + ipset_init_library(); + + ipset_init(&white_list_ipv4); + ipset_init(&white_list_ipv6); + ipset_init(&black_list_ipv4); + ipset_init(&black_list_ipv6); + ipset_init(&outbound_block_list_ipv4); + ipset_init(&outbound_block_list_ipv6); + + cork_dllist_init(&black_list_rules); + cork_dllist_init(&white_list_rules); + cork_dllist_init(&outbound_block_list_rules); + + struct ip_set *list_ipv4 = &black_list_ipv4; + struct ip_set *list_ipv6 = &black_list_ipv6; + struct cork_dllist *rules = &black_list_rules; + + FILE *f = fopen(path, "r"); + if (f == NULL) { + LOGE("Invalid acl path."); + return -1; + } + + char buf[257]; + while (!feof(f)) + if (fgets(buf, 256, f)) { + // Trim the newline + int len = strlen(buf); + if (len > 0 && buf[len - 1] == '\n') { + buf[len - 1] = '\0'; + } + + char *line = trimwhitespace(buf); + + // Skip comments + if (line[0] == '#') { + continue; + } + + if (strlen(line) == 0) { + continue; + } + + if (strcmp(line, "[outbound_block_list]") == 0) { + list_ipv4 = &outbound_block_list_ipv4; + list_ipv6 = &outbound_block_list_ipv6; + rules = &outbound_block_list_rules; + continue; + } else if (strcmp(line, "[black_list]") == 0 + || strcmp(line, "[bypass_list]") == 0) { + list_ipv4 = &black_list_ipv4; + list_ipv6 = &black_list_ipv6; + rules = &black_list_rules; + continue; + } else if (strcmp(line, "[white_list]") == 0 + || strcmp(line, "[proxy_list]") == 0) { + list_ipv4 = &white_list_ipv4; + list_ipv6 = &white_list_ipv6; + rules = &white_list_rules; + continue; + } else if (strcmp(line, "[reject_all]") == 0 + || strcmp(line, "[bypass_all]") == 0) { + acl_mode = WHITE_LIST; + continue; + } else if (strcmp(line, "[accept_all]") == 0 + || strcmp(line, "[proxy_all]") == 0) { + acl_mode = BLACK_LIST; + continue; + } + + char host[257]; + int cidr; + parse_addr_cidr(line, host, &cidr); + + struct cork_ip addr; + int err = cork_ip_init(&addr, host); + if (!err) { + if (addr.version == 4) { + if (cidr >= 0) { + ipset_ipv4_add_network(list_ipv4, &(addr.ip.v4), cidr); + } else { + ipset_ipv4_add(list_ipv4, &(addr.ip.v4)); + } + } else if (addr.version == 6) { + if (cidr >= 0) { + ipset_ipv6_add_network(list_ipv6, &(addr.ip.v6), cidr); + } else { + ipset_ipv6_add(list_ipv6, &(addr.ip.v6)); + } + } + } else { + rule_t *rule = new_rule(); + accept_rule_arg(rule, line); + init_rule(rule); + add_rule(rules, rule); + } + } + + fclose(f); + + return 0; +} + +void +free_rules(struct cork_dllist *rules) +{ + struct cork_dllist_item *iter; + while ((iter = cork_dllist_head(rules)) != NULL) { + rule_t *rule = cork_container_of(iter, rule_t, entries); + remove_rule(rule); + } +} + +void +free_acl(void) +{ + ipset_done(&black_list_ipv4); + ipset_done(&black_list_ipv6); + ipset_done(&white_list_ipv4); + ipset_done(&white_list_ipv6); + + free_rules(&black_list_rules); + free_rules(&white_list_rules); +} + +int +get_acl_mode(void) +{ + return acl_mode; +} + +/* + * Return 0, if not match. + * Return 1, if match black list. + * Return -1, if match white list. + */ +int +acl_match_host(const char *host) +{ + struct cork_ip addr; + int ret = 0; + int err = cork_ip_init(&addr, host); + + if (err) { + int host_len = strlen(host); + if (lookup_rule(&black_list_rules, host, host_len) != NULL) + ret = 1; + else if (lookup_rule(&white_list_rules, host, host_len) != NULL) + ret = -1; + return ret; + } + + if (addr.version == 4) { + if (ipset_contains_ipv4(&black_list_ipv4, &(addr.ip.v4))) + ret = 1; + else if (ipset_contains_ipv4(&white_list_ipv4, &(addr.ip.v4))) + ret = -1; + } else if (addr.version == 6) { + if (ipset_contains_ipv6(&black_list_ipv6, &(addr.ip.v6))) + ret = 1; + else if (ipset_contains_ipv6(&white_list_ipv6, &(addr.ip.v6))) + ret = -1; + } + + return ret; +} + +int +acl_add_ip(const char *ip) +{ + struct cork_ip addr; + int err = cork_ip_init(&addr, ip); + if (err) { + return -1; + } + + if (addr.version == 4) { + ipset_ipv4_add(&black_list_ipv4, &(addr.ip.v4)); + } else if (addr.version == 6) { + ipset_ipv6_add(&black_list_ipv6, &(addr.ip.v6)); + } + + return 0; +} + +int +acl_remove_ip(const char *ip) +{ + struct cork_ip addr; + int err = cork_ip_init(&addr, ip); + if (err) { + return -1; + } + + if (addr.version == 4) { + ipset_ipv4_remove(&black_list_ipv4, &(addr.ip.v4)); + } else if (addr.version == 6) { + ipset_ipv6_remove(&black_list_ipv6, &(addr.ip.v6)); + } + + return 0; +} + +/* + * Return 0, if not match. + * Return 1, if match black list. + */ +int +outbound_block_match_host(const char *host) +{ + struct cork_ip addr; + int ret = 0; + int err = cork_ip_init(&addr, host); + + if (err) { + int host_len = strlen(host); + if (lookup_rule(&outbound_block_list_rules, host, host_len) != NULL) + ret = 1; + return ret; + } + + if (addr.version == 4) { + if (ipset_contains_ipv4(&outbound_block_list_ipv4, &(addr.ip.v4))) + ret = 1; + } else if (addr.version == 6) { + if (ipset_contains_ipv6(&outbound_block_list_ipv6, &(addr.ip.v6))) + ret = 1; + } + + return ret; +} diff --git a/shadowsocksr-libev/src/server/acl.h b/shadowsocksr-libev/src/server/acl.h new file mode 100644 index 000000000..d6f18b807 --- /dev/null +++ b/shadowsocksr-libev/src/server/acl.h @@ -0,0 +1,53 @@ +/* + * acl.h - Define the ACL interface + * + * Copyright (C) 2013 - 2016, Max Lv + * + * This file is part of the shadowsocks-libev. + * + * shadowsocks-libev is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * shadowsocks-libev 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with shadowsocks-libev; see the file COPYING. If not, see + * . + */ + +#ifndef _ACL_H +#define _ACL_H + +#define BLACK_LIST 0 +#define WHITE_LIST 1 + +#define MAX_TRIES 64 +#define MALICIOUS 8 +#define SUSPICIOUS 4 +#define BAD 2 +#define MALFORMED 1 + +int init_acl(const char *path); +void free_acl(void); +void clear_block_list(void); + +int acl_match_host(const char *ip); +int acl_add_ip(const char *ip); +int acl_remove_ip(const char *ip); + +int get_acl_mode(void); + +void init_block_list(int firewall); +void free_block_list(); +int check_block_list(char *addr); +int update_block_list(char *addr, int err_level); +int remove_from_block_list(char *addr); + +int outbound_block_match_host(const char *host); + +#endif // _ACL_H diff --git a/shadowsocksr-libev/src/server/auth.c b/shadowsocksr-libev/src/server/auth.c new file mode 100644 index 000000000..a36257a9e --- /dev/null +++ b/shadowsocksr-libev/src/server/auth.c @@ -0,0 +1,993 @@ + +#include "auth.h" + +static int auth_simple_pack_unit_size = 2000; +typedef int (*hmac_with_key_func)(char *auth, char *msg, int msg_len, uint8_t *auth_key, int key_len); +typedef int (*hash_func)(char *auth, char *msg, int msg_len); + +typedef struct auth_simple_global_data { + uint8_t local_client_id[8]; + uint32_t connection_id; +}auth_simple_global_data; + +typedef struct auth_simple_local_data { + int has_sent_header; + char * recv_buffer; + int recv_buffer_size; + uint32_t recv_id; + uint32_t pack_id; + char * salt; + uint8_t * user_key; + char uid[4]; + int user_key_len; + hmac_with_key_func hmac; + hash_func hash; + int hash_len; +}auth_simple_local_data; + +void auth_simple_local_data_init(auth_simple_local_data* local) { + local->has_sent_header = 0; + local->recv_buffer = (char*)malloc(16384); + local->recv_buffer_size = 0; + local->recv_id = 1; + local->pack_id = 1; + local->salt = ""; + local->user_key = 0; + local->user_key_len = 0; + local->hmac = 0; + local->hash = 0; + local->hash_len = 0; + local->salt = ""; +} + +void * auth_simple_init_data() { + auth_simple_global_data *global = (auth_simple_global_data*)malloc(sizeof(auth_simple_global_data)); + rand_bytes(global->local_client_id, 8); + rand_bytes((uint8_t*)&global->connection_id, 4); + global->connection_id &= 0xFFFFFF; + return global; +} + +obfs * auth_simple_new_obfs() { + obfs * self = new_obfs(); + self->l_data = malloc(sizeof(auth_simple_local_data)); + auth_simple_local_data_init((auth_simple_local_data*)self->l_data); + return self; +} + +obfs * auth_aes128_md5_new_obfs() { + obfs * self = new_obfs(); + self->l_data = malloc(sizeof(auth_simple_local_data)); + auth_simple_local_data_init((auth_simple_local_data*)self->l_data); + ((auth_simple_local_data*)self->l_data)->hmac = ss_md5_hmac_with_key; + ((auth_simple_local_data*)self->l_data)->hash = ss_md5_hash_func; + ((auth_simple_local_data*)self->l_data)->hash_len = 16; + ((auth_simple_local_data*)self->l_data)->salt = "auth_aes128_md5"; + return self; +} + +obfs * auth_aes128_sha1_new_obfs() { + obfs * self = new_obfs(); + self->l_data = malloc(sizeof(auth_simple_local_data)); + auth_simple_local_data_init((auth_simple_local_data*)self->l_data); + ((auth_simple_local_data*)self->l_data)->hmac = ss_sha1_hmac_with_key; + ((auth_simple_local_data*)self->l_data)->hash = ss_sha1_hash_func; + ((auth_simple_local_data*)self->l_data)->hash_len = 20; + ((auth_simple_local_data*)self->l_data)->salt = "auth_aes128_sha1"; + return self; +} + +void auth_simple_dispose(obfs *self) { + auth_simple_local_data *local = (auth_simple_local_data*)self->l_data; + if (local->recv_buffer != NULL) { + free(local->recv_buffer); + local->recv_buffer = NULL; + } + if (local->user_key != NULL) { + free(local->user_key); + local->user_key = NULL; + } + free(local); + self->l_data = NULL; + dispose_obfs(self); +} + +int auth_simple_pack_data(char *data, int datalength, char *outdata) { + unsigned char rand_len = (xorshift128plus() & 0xF) + 1; + int out_size = rand_len + datalength + 6; + outdata[0] = out_size >> 8; + outdata[1] = out_size; + outdata[2] = rand_len; + memmove(outdata + rand_len + 2, data, datalength); + fillcrc32((unsigned char *)outdata, out_size); + return out_size; +} + +void memintcopy_lt(void *mem, uint32_t val) { + ((uint8_t *)mem)[0] = val; + ((uint8_t *)mem)[1] = val >> 8; + ((uint8_t *)mem)[2] = val >> 16; + ((uint8_t *)mem)[3] = val >> 24; +} + +int auth_simple_pack_auth_data(auth_simple_global_data *global, char *data, int datalength, char *outdata) { + unsigned char rand_len = (xorshift128plus() & 0xF) + 1; + int out_size = rand_len + datalength + 6 + 12; + outdata[0] = out_size >> 8; + outdata[1] = out_size; + outdata[2] = rand_len; + ++global->connection_id; + if (global->connection_id > 0xFF000000) { + rand_bytes(global->local_client_id, 8); + rand_bytes((uint8_t*)&global->connection_id, 4); + global->connection_id &= 0xFFFFFF; + } + time_t t = time(NULL); + memintcopy_lt(outdata + rand_len + 2, t); + memmove(outdata + rand_len + 2 + 4, global->local_client_id, 4); + memintcopy_lt(outdata + rand_len + 2 + 8, global->connection_id); + memmove(outdata + rand_len + 2 + 12, data, datalength); + fillcrc32((unsigned char *)outdata, out_size); + return out_size; +} + +int auth_simple_client_pre_encrypt(obfs *self, char **pplaindata, int datalength, size_t* capacity) { + char *plaindata = *pplaindata; + auth_simple_local_data *local = (auth_simple_local_data*)self->l_data; + char * out_buffer = (char*)malloc(datalength * 2 + 64); + char * buffer = out_buffer; + char * data = plaindata; + int len = datalength; + int pack_len; + if (len > 0 && local->has_sent_header == 0) { + int head_size = get_head_size(plaindata, datalength, 30); + if (head_size > datalength) + head_size = datalength; + pack_len = auth_simple_pack_auth_data((auth_simple_global_data *)self->server.g_data, data, head_size, buffer); + buffer += pack_len; + data += head_size; + len -= head_size; + local->has_sent_header = 1; + } + while ( len > auth_simple_pack_unit_size ) { + pack_len = auth_simple_pack_data(data, auth_simple_pack_unit_size, buffer); + buffer += pack_len; + data += auth_simple_pack_unit_size; + len -= auth_simple_pack_unit_size; + } + if (len > 0) { + pack_len = auth_simple_pack_data(data, len, buffer); + buffer += pack_len; + } + len = buffer - out_buffer; + if (*capacity < len) { + *pplaindata = (char*)realloc(*pplaindata, *capacity = len * 2); + plaindata = *pplaindata; + } + memmove(plaindata, out_buffer, len); + free(out_buffer); + return len; +} + +int auth_simple_client_post_decrypt(obfs *self, char **pplaindata, int datalength, size_t* capacity) { + char *plaindata = *pplaindata; + auth_simple_local_data *local = (auth_simple_local_data*)self->l_data; + uint8_t * recv_buffer = (uint8_t *)local->recv_buffer; + if (local->recv_buffer_size + datalength > 16384) + return -1; + memmove(recv_buffer + local->recv_buffer_size, plaindata, datalength); + local->recv_buffer_size += datalength; + + char * out_buffer = (char*)malloc(local->recv_buffer_size); + char * buffer = out_buffer; + while (local->recv_buffer_size > 2) { + int length = ((int)recv_buffer[0] << 8) | recv_buffer[1]; + if (length >= 8192 || length < 7) { + free(out_buffer); + local->recv_buffer_size = 0; + return -1; + } + if (length > local->recv_buffer_size) + break; + + int crc = crc32((unsigned char*)recv_buffer, length); + if (crc != -1) { + free(out_buffer); + local->recv_buffer_size = 0; + return -1; + } + int data_size = length - recv_buffer[2] - 6; + memmove(buffer, recv_buffer + 2 + recv_buffer[2], data_size); + buffer += data_size; + memmove(recv_buffer, recv_buffer + length, local->recv_buffer_size -= length); + } + int len = buffer - out_buffer; + if (*capacity < len) { + *pplaindata = (char*)realloc(*pplaindata, *capacity = len * 2); + plaindata = *pplaindata; + } + memmove(plaindata, out_buffer, len); + free(out_buffer); + return len; +} + + +int auth_sha1_pack_data(char *data, int datalength, char *outdata) { + unsigned char rand_len = (xorshift128plus() & 0xF) + 1; + int out_size = rand_len + datalength + 6; + outdata[0] = out_size >> 8; + outdata[1] = out_size; + outdata[2] = rand_len; + memmove(outdata + rand_len + 2, data, datalength); + filladler32((unsigned char *)outdata, out_size); + return out_size; +} + +int auth_sha1_pack_auth_data(auth_simple_global_data *global, server_info *server, char *data, int datalength, char *outdata) { + unsigned char rand_len = (xorshift128plus() & 0x7F) + 1; + int data_offset = rand_len + 4 + 2; + int out_size = data_offset + datalength + 12 + OBFS_HMAC_SHA1_LEN; + fillcrc32to((unsigned char *)server->key, server->key_len, (unsigned char *)outdata); + outdata[4] = out_size >> 8; + outdata[5] = out_size; + outdata[6] = rand_len; + ++global->connection_id; + if (global->connection_id > 0xFF000000) { + rand_bytes(global->local_client_id, 8); + rand_bytes((uint8_t*)&global->connection_id, 4); + global->connection_id &= 0xFFFFFF; + } + time_t t = time(NULL); + memintcopy_lt(outdata + data_offset, t); + memmove(outdata + data_offset + 4, global->local_client_id, 4); + memintcopy_lt(outdata + data_offset + 8, global->connection_id); + memmove(outdata + data_offset + 12, data, datalength); + char hash[ONETIMEAUTH_BYTES * 2]; + ss_sha1_hmac(hash, outdata, out_size - OBFS_HMAC_SHA1_LEN, server->iv); + memcpy(outdata + out_size - OBFS_HMAC_SHA1_LEN, hash, OBFS_HMAC_SHA1_LEN); + return out_size; +} + +int auth_sha1_client_pre_encrypt(obfs *self, char **pplaindata, int datalength, size_t* capacity) { + char *plaindata = *pplaindata; + auth_simple_local_data *local = (auth_simple_local_data*)self->l_data; + char * out_buffer = (char*)malloc(datalength * 2 + 256); + char * buffer = out_buffer; + char * data = plaindata; + int len = datalength; + int pack_len; + if (len > 0 && local->has_sent_header == 0) { + int head_size = get_head_size(plaindata, datalength, 30); + if (head_size > datalength) + head_size = datalength; + pack_len = auth_sha1_pack_auth_data((auth_simple_global_data *)self->server.g_data, &self->server, data, head_size, buffer); + buffer += pack_len; + data += head_size; + len -= head_size; + local->has_sent_header = 1; + } + while ( len > auth_simple_pack_unit_size ) { + pack_len = auth_sha1_pack_data(data, auth_simple_pack_unit_size, buffer); + buffer += pack_len; + data += auth_simple_pack_unit_size; + len -= auth_simple_pack_unit_size; + } + if (len > 0) { + pack_len = auth_sha1_pack_data(data, len, buffer); + buffer += pack_len; + } + len = buffer - out_buffer; + if (*capacity < len) { + *pplaindata = (char*)realloc(*pplaindata, *capacity = len * 2); + plaindata = *pplaindata; + } + memmove(plaindata, out_buffer, len); + free(out_buffer); + return len; +} + +int auth_sha1_client_post_decrypt(obfs *self, char **pplaindata, int datalength, size_t* capacity) { + char *plaindata = *pplaindata; + auth_simple_local_data *local = (auth_simple_local_data*)self->l_data; + uint8_t * recv_buffer = (uint8_t *)local->recv_buffer; + if (local->recv_buffer_size + datalength > 16384) + return -1; + memmove(recv_buffer + local->recv_buffer_size, plaindata, datalength); + local->recv_buffer_size += datalength; + + char * out_buffer = (char*)malloc(local->recv_buffer_size); + char * buffer = out_buffer; + while (local->recv_buffer_size > 2) { + int length = ((int)recv_buffer[0] << 8) | recv_buffer[1]; + if (length >= 8192 || length < 7) { + free(out_buffer); + local->recv_buffer_size = 0; + return -1; + } + if (length > local->recv_buffer_size) + break; + + if (checkadler32((unsigned char*)recv_buffer, length) == 0) { + free(out_buffer); + local->recv_buffer_size = 0; + return -1; + } + int pos = recv_buffer[2] + 2; + int data_size = length - pos - 4; + memmove(buffer, recv_buffer + pos, data_size); + buffer += data_size; + memmove(recv_buffer, recv_buffer + length, local->recv_buffer_size -= length); + } + int len = buffer - out_buffer; + if (*capacity < len) { + *pplaindata = (char*)realloc(*pplaindata, *capacity = len * 2); + plaindata = *pplaindata; + } + memmove(plaindata, out_buffer, len); + free(out_buffer); + return len; +} + +int auth_sha1_v2_pack_data(char *data, int datalength, char *outdata) { + unsigned int rand_len = (datalength > 1300 ? 0 : datalength > 400 ? (xorshift128plus() & 0x7F) : (xorshift128plus() & 0x3FF)) + 1; + int out_size = rand_len + datalength + 6; + outdata[0] = out_size >> 8; + outdata[1] = out_size; + if (rand_len < 128) + { + outdata[2] = rand_len; + } + else + { + outdata[2] = 0xFF; + outdata[3] = rand_len >> 8; + outdata[4] = rand_len; + } + memmove(outdata + rand_len + 2, data, datalength); + filladler32((unsigned char *)outdata, out_size); + return out_size; +} + +int auth_sha1_v2_pack_auth_data(auth_simple_global_data *global, server_info *server, char *data, int datalength, char *outdata) { + unsigned int rand_len = (datalength > 1300 ? 0 : datalength > 400 ? (xorshift128plus() & 0x7F) : (xorshift128plus() & 0x3FF)) + 1; + int data_offset = rand_len + 4 + 2; + int out_size = data_offset + datalength + 12 + OBFS_HMAC_SHA1_LEN; + const char* salt = "auth_sha1_v2"; + int salt_len = strlen(salt); + unsigned char *crc_salt = (unsigned char*)malloc(salt_len + server->key_len); + memcpy(crc_salt, salt, salt_len); + memcpy(crc_salt + salt_len, server->key, server->key_len); + fillcrc32to(crc_salt, salt_len + server->key_len, (unsigned char *)outdata); + free(crc_salt); + outdata[4] = out_size >> 8; + outdata[5] = out_size; + if (rand_len < 128) + { + outdata[6] = rand_len; + } + else + { + outdata[6] = 0xFF; + outdata[7] = rand_len >> 8; + outdata[8] = rand_len; + } + ++global->connection_id; + if (global->connection_id > 0xFF000000) { + rand_bytes(global->local_client_id, 8); + rand_bytes((uint8_t*)&global->connection_id, 4); + global->connection_id &= 0xFFFFFF; + } + memmove(outdata + data_offset, global->local_client_id, 8); + memintcopy_lt(outdata + data_offset + 8, global->connection_id); + memmove(outdata + data_offset + 12, data, datalength); + char hash[ONETIMEAUTH_BYTES * 2]; + ss_sha1_hmac(hash, outdata, out_size - OBFS_HMAC_SHA1_LEN, server->iv); + memcpy(outdata + out_size - OBFS_HMAC_SHA1_LEN, hash, OBFS_HMAC_SHA1_LEN); + return out_size; +} + +int auth_sha1_v2_client_pre_encrypt(obfs *self, char **pplaindata, int datalength, size_t* capacity) { + char *plaindata = *pplaindata; + auth_simple_local_data *local = (auth_simple_local_data*)self->l_data; + char * out_buffer = (char*)malloc(datalength * 2 + 4096); + char * buffer = out_buffer; + char * data = plaindata; + int len = datalength; + int pack_len; + if (len > 0 && local->has_sent_header == 0) { + int head_size = get_head_size(plaindata, datalength, 30); + if (head_size > datalength) + head_size = datalength; + pack_len = auth_sha1_v2_pack_auth_data((auth_simple_global_data *)self->server.g_data, &self->server, data, head_size, buffer); + buffer += pack_len; + data += head_size; + len -= head_size; + local->has_sent_header = 1; + } + while ( len > auth_simple_pack_unit_size ) { + pack_len = auth_sha1_v2_pack_data(data, auth_simple_pack_unit_size, buffer); + buffer += pack_len; + data += auth_simple_pack_unit_size; + len -= auth_simple_pack_unit_size; + } + if (len > 0) { + pack_len = auth_sha1_v2_pack_data(data, len, buffer); + buffer += pack_len; + } + len = buffer - out_buffer; + if (*capacity < len) { + *pplaindata = (char*)realloc(*pplaindata, *capacity = len * 2); + plaindata = *pplaindata; + } + memmove(plaindata, out_buffer, len); + free(out_buffer); + return len; +} + +int auth_sha1_v2_client_post_decrypt(obfs *self, char **pplaindata, int datalength, size_t* capacity) { + char *plaindata = *pplaindata; + auth_simple_local_data *local = (auth_simple_local_data*)self->l_data; + uint8_t * recv_buffer = (uint8_t *)local->recv_buffer; + if (local->recv_buffer_size + datalength > 16384) + return -1; + memmove(recv_buffer + local->recv_buffer_size, plaindata, datalength); + local->recv_buffer_size += datalength; + + char * out_buffer = (char*)malloc(local->recv_buffer_size); + char * buffer = out_buffer; + char error = 0; + while (local->recv_buffer_size > 2) { + int length = ((int)recv_buffer[0] << 8) | recv_buffer[1]; + if (length >= 8192 || length < 7) { + local->recv_buffer_size = 0; + error = 1; + break; + } + if (length > local->recv_buffer_size) + break; + + if (checkadler32((unsigned char*)recv_buffer, length) == 0) { + local->recv_buffer_size = 0; + error = 1; + break; + } + int pos = recv_buffer[2]; + if (pos < 255) + { + pos += 2; + } + else + { + pos = ((recv_buffer[3] << 8) | recv_buffer[4]) + 2; + } + int data_size = length - pos - 4; + memmove(buffer, recv_buffer + pos, data_size); + buffer += data_size; + memmove(recv_buffer, recv_buffer + length, local->recv_buffer_size -= length); + } + int len; + if (error == 0) { + len = buffer - out_buffer; + if (*capacity < len) { + *pplaindata = (char*)realloc(*pplaindata, *capacity = len * 2); + plaindata = *pplaindata; + } + memmove(plaindata, out_buffer, len); + } else { + len = -1; + } + free(out_buffer); + return len; +} + +int auth_sha1_v4_pack_data(char *data, int datalength, char *outdata) { + unsigned int rand_len = (datalength > 1300 ? 0 : datalength > 400 ? (xorshift128plus() & 0x7F) : (xorshift128plus() & 0x3FF)) + 1; + int out_size = rand_len + datalength + 8; + outdata[0] = out_size >> 8; + outdata[1] = out_size; + uint32_t crc_val = crc32((unsigned char*)outdata, 2); + outdata[2] = crc_val; + outdata[3] = crc_val >> 8; + if (rand_len < 128) + { + outdata[4] = rand_len; + } + else + { + outdata[4] = 0xFF; + outdata[5] = rand_len >> 8; + outdata[6] = rand_len; + } + memmove(outdata + rand_len + 4, data, datalength); + filladler32((unsigned char *)outdata, out_size); + return out_size; +} + +int auth_sha1_v4_pack_auth_data(auth_simple_global_data *global, server_info *server, char *data, int datalength, char *outdata) { + unsigned int rand_len = (datalength > 1300 ? 0 : datalength > 400 ? (xorshift128plus() & 0x7F) : (xorshift128plus() & 0x3FF)) + 1; + int data_offset = rand_len + 4 + 2; + int out_size = data_offset + datalength + 12 + OBFS_HMAC_SHA1_LEN; + const char* salt = "auth_sha1_v4"; + int salt_len = strlen(salt); + unsigned char *crc_salt = (unsigned char*)malloc(salt_len + server->key_len + 2); + crc_salt[0] = outdata[0] = out_size >> 8; + crc_salt[1] = outdata[1] = out_size; + + memcpy(crc_salt + 2, salt, salt_len); + memcpy(crc_salt + salt_len + 2, server->key, server->key_len); + fillcrc32to(crc_salt, salt_len + server->key_len + 2, (unsigned char *)outdata + 2); + free(crc_salt); + if (rand_len < 128) + { + outdata[6] = rand_len; + } + else + { + outdata[6] = 0xFF; + outdata[7] = rand_len >> 8; + outdata[8] = rand_len; + } + ++global->connection_id; + if (global->connection_id > 0xFF000000) { + rand_bytes(global->local_client_id, 8); + rand_bytes((uint8_t*)&global->connection_id, 4); + global->connection_id &= 0xFFFFFF; + } + time_t t = time(NULL); + memintcopy_lt(outdata + data_offset, t); + memmove(outdata + data_offset + 4, global->local_client_id, 4); + memintcopy_lt(outdata + data_offset + 8, global->connection_id); + memmove(outdata + data_offset + 12, data, datalength); + char hash[ONETIMEAUTH_BYTES * 2]; + ss_sha1_hmac(hash, outdata, out_size - OBFS_HMAC_SHA1_LEN, server->iv); + memcpy(outdata + out_size - OBFS_HMAC_SHA1_LEN, hash, OBFS_HMAC_SHA1_LEN); + return out_size; +} + +int auth_sha1_v4_client_pre_encrypt(obfs *self, char **pplaindata, int datalength, size_t* capacity) { + char *plaindata = *pplaindata; + auth_simple_local_data *local = (auth_simple_local_data*)self->l_data; + char * out_buffer = (char*)malloc(datalength * 2 + 4096); + char * buffer = out_buffer; + char * data = plaindata; + int len = datalength; + int pack_len; + if (len > 0 && local->has_sent_header == 0) { + int head_size = get_head_size(plaindata, datalength, 30); + if (head_size > datalength) + head_size = datalength; + pack_len = auth_sha1_v4_pack_auth_data((auth_simple_global_data *)self->server.g_data, &self->server, data, head_size, buffer); + buffer += pack_len; + data += head_size; + len -= head_size; + local->has_sent_header = 1; + } + while ( len > auth_simple_pack_unit_size ) { + pack_len = auth_sha1_v4_pack_data(data, auth_simple_pack_unit_size, buffer); + buffer += pack_len; + data += auth_simple_pack_unit_size; + len -= auth_simple_pack_unit_size; + } + if (len > 0) { + pack_len = auth_sha1_v4_pack_data(data, len, buffer); + buffer += pack_len; + } + len = buffer - out_buffer; + if (*capacity < len) { + *pplaindata = (char*)realloc(*pplaindata, *capacity = len * 2); + plaindata = *pplaindata; + } + memmove(plaindata, out_buffer, len); + free(out_buffer); + return len; +} + +int auth_sha1_v4_client_post_decrypt(obfs *self, char **pplaindata, int datalength, size_t* capacity) { + char *plaindata = *pplaindata; + auth_simple_local_data *local = (auth_simple_local_data*)self->l_data; + uint8_t * recv_buffer = (uint8_t *)local->recv_buffer; + if (local->recv_buffer_size + datalength > 16384) + return -1; + memmove(recv_buffer + local->recv_buffer_size, plaindata, datalength); + local->recv_buffer_size += datalength; + + char * out_buffer = (char*)malloc(local->recv_buffer_size); + char * buffer = out_buffer; + char error = 0; + while (local->recv_buffer_size > 4) { + uint32_t crc_val = crc32((unsigned char*)recv_buffer, 2); + if ((((uint32_t)recv_buffer[3] << 8) | recv_buffer[2]) != (crc_val & 0xffff)) { + local->recv_buffer_size = 0; + error = 1; + break; + } + int length = ((int)recv_buffer[0] << 8) | recv_buffer[1]; + if (length >= 8192 || length < 7) { + local->recv_buffer_size = 0; + error = 1; + break; + } + if (length > local->recv_buffer_size) + break; + + if (checkadler32((unsigned char*)recv_buffer, length) == 0) { + local->recv_buffer_size = 0; + error = 1; + break; + } + int pos = recv_buffer[4]; + if (pos < 255) + { + pos += 4; + } + else + { + pos = (((int)recv_buffer[5] << 8) | recv_buffer[6]) + 4; + } + int data_size = length - pos - 4; + memmove(buffer, recv_buffer + pos, data_size); + buffer += data_size; + memmove(recv_buffer, recv_buffer + length, local->recv_buffer_size -= length); + } + int len; + if (error == 0) { + len = buffer - out_buffer; + if (*capacity < len) { + *pplaindata = (char*)realloc(*pplaindata, *capacity = len * 2); + plaindata = *pplaindata; + } + memmove(plaindata, out_buffer, len); + } else { + len = -1; + } + free(out_buffer); + return len; +} + + +int auth_aes128_sha1_pack_data(char *data, int datalength, char *outdata, auth_simple_local_data *local, server_info *server) { + unsigned int rand_len = (datalength > 1200 ? 0 : local->pack_id > 4 ? (xorshift128plus() & 0x20) : datalength > 900 ? (xorshift128plus() & 0x80) : (xorshift128plus() & 0x200)) + 1; + int out_size = rand_len + datalength + 8; + memcpy(outdata + rand_len + 4, data, datalength); + outdata[0] = out_size; + outdata[1] = out_size >> 8; + uint8_t key_len = local->user_key_len + 4; + uint8_t *key = (uint8_t*)malloc(key_len); + memcpy(key, local->user_key, local->user_key_len); + memintcopy_lt(key + key_len - 4, local->pack_id); + + { + uint8_t rnd_data[rand_len]; + rand_bytes(rnd_data, rand_len); + memcpy(outdata + 4, rnd_data, rand_len); + } + + { + char hash[20]; + local->hmac(hash, outdata, 2, key, key_len); + memcpy(outdata + 2, hash, 2); + } + + if (rand_len < 128) + { + outdata[4] = rand_len; + } + else + { + outdata[4] = 0xFF; + outdata[5] = rand_len; + outdata[6] = rand_len >> 8; + } + ++local->pack_id; + + { + char hash[20]; + local->hmac(hash, outdata, out_size - 4, key, key_len); + memcpy(outdata + out_size - 4, hash, 4); + } + free(key); + + return out_size; +} + +int auth_aes128_sha1_pack_auth_data(auth_simple_global_data *global, server_info *server, auth_simple_local_data *local, char *data, int datalength, char *outdata) { + unsigned int rand_len = (datalength > 400 ? (xorshift128plus() & 0x200) : (xorshift128plus() & 0x400)); + int data_offset = rand_len + 16 + 4 + 4 + 7; + int out_size = data_offset + datalength + 4; + + char encrypt[24]; + char encrypt_data[16]; + + uint8_t *key = (uint8_t*)malloc(server->iv_len + server->key_len); + uint8_t key_len = server->iv_len + server->key_len; + memcpy(key, server->iv, server->iv_len); + memcpy(key + server->iv_len, server->key, server->key_len); + + { + uint8_t rnd_data[rand_len]; + rand_bytes(rnd_data, rand_len); + memcpy(outdata + data_offset - rand_len, rnd_data, rand_len); + } + + ++global->connection_id; + if (global->connection_id > 0xFF000000) { + rand_bytes(global->local_client_id, 8); + rand_bytes((uint8_t*)&global->connection_id, 4); + global->connection_id &= 0xFFFFFF; + } + time_t t = time(NULL); + memintcopy_lt(encrypt, t); + memcpy(encrypt + 4, global->local_client_id, 4); + memintcopy_lt(encrypt + 8, global->connection_id); + encrypt[12] = out_size; + encrypt[13] = out_size >> 8; + encrypt[14] = rand_len; + encrypt[15] = rand_len >> 8; + + { + + if (local->user_key == NULL) { + if(server->param != NULL && server->param[0] != 0) { + char *param = server->param; + char *delim = strchr(param, ':'); + if(delim != NULL) { + char uid_str[16] = {}; + strncpy(uid_str, param, delim - param); + char key_str[128]; + strcpy(key_str, delim + 1); + long uid_long = strtol(uid_str, NULL, 10); + memintcopy_lt(local->uid, uid_long); + + char hash[21] = {0}; + local->hash(hash, key_str, strlen(key_str)); + + local->user_key_len = local->hash_len; + local->user_key = (uint8_t*)malloc(local->user_key_len); + memcpy(local->user_key, hash, local->hash_len); + } + } + if (local->user_key == NULL) { + rand_bytes((uint8_t *)local->uid, 4); + + local->user_key_len = server->key_len; + local->user_key = (uint8_t*)malloc(local->user_key_len); + memcpy(local->user_key, server->key, local->user_key_len); + } + } + + char encrypt_key_base64[256] = {0}; + unsigned char encrypt_key[local->user_key_len]; + memcpy(encrypt_key, local->user_key, local->user_key_len); + base64_encode(encrypt_key, local->user_key_len, encrypt_key_base64); + + int base64_len; + base64_len = (local->user_key_len + 2) / 3 * 4; + memcpy(encrypt_key_base64 + base64_len, local->salt, strlen(local->salt)); + + char enc_key[16]; + int enc_key_len = base64_len + strlen(local->salt); + bytes_to_key_with_size(encrypt_key_base64, enc_key_len, (uint8_t*)enc_key, 16); + ss_aes_128_cbc(encrypt, encrypt_data, enc_key); + memcpy(encrypt + 4, encrypt_data, 16); + memcpy(encrypt, local->uid, 4); + } + + { + char hash[20]; + local->hmac(hash, encrypt, 20, key, key_len); + memcpy(encrypt + 20, hash, 4); + } + + { + uint8_t rnd[1]; + rand_bytes(rnd, 1); + memcpy(outdata, rnd, 1); + char hash[20]; + local->hmac(hash, (char *)rnd, 1, key, key_len); + memcpy(outdata + 1, hash, 6); + } + + memcpy(outdata + 7, encrypt, 24); + memcpy(outdata + data_offset, data, datalength); + + { + char hash[20]; + local->hmac(hash, outdata, out_size - 4, local->user_key, local->user_key_len); + memmove(outdata + out_size - 4, hash, 4); + } + free(key); + + return out_size; +} + +int auth_aes128_sha1_client_pre_encrypt(obfs *self, char **pplaindata, int datalength, size_t* capacity) { + char *plaindata = *pplaindata; + auth_simple_local_data *local = (auth_simple_local_data*)self->l_data; + char * out_buffer = (char*)malloc(datalength * 2 + 4096); + char * buffer = out_buffer; + char * data = plaindata; + int len = datalength; + int pack_len; + if (len > 0 && local->has_sent_header == 0) { + int head_size = 1200; + if (head_size > datalength) + head_size = datalength; + pack_len = auth_aes128_sha1_pack_auth_data((auth_simple_global_data *)self->server.g_data, &self->server, local, data, head_size, buffer); + buffer += pack_len; + data += head_size; + len -= head_size; + local->has_sent_header = 1; + } + while ( len > auth_simple_pack_unit_size ) { + pack_len = auth_aes128_sha1_pack_data(data, auth_simple_pack_unit_size, buffer, local, &self->server); + buffer += pack_len; + data += auth_simple_pack_unit_size; + len -= auth_simple_pack_unit_size; + } + if (len > 0) { + pack_len = auth_aes128_sha1_pack_data(data, len, buffer, local, &self->server); + buffer += pack_len; + } + len = buffer - out_buffer; + if (*capacity < len) { + *pplaindata = (char*)realloc(*pplaindata, *capacity = len * 2); + plaindata = *pplaindata; + } + memmove(plaindata, out_buffer, len); + free(out_buffer); + return len; +} + +int auth_aes128_sha1_client_post_decrypt(obfs *self, char **pplaindata, int datalength, size_t* capacity) { + char *plaindata = *pplaindata; + auth_simple_local_data *local = (auth_simple_local_data*)self->l_data; + //server_info *server = (server_info*)&self->server; + uint8_t * recv_buffer = (uint8_t *)local->recv_buffer; + if (local->recv_buffer_size + datalength > 16384) + return -1; + memmove(recv_buffer + local->recv_buffer_size, plaindata, datalength); + local->recv_buffer_size += datalength; + + int key_len = local->user_key_len + 4; + uint8_t *key = (uint8_t*)malloc(key_len); + memcpy(key, local->user_key, local->user_key_len); + + char * out_buffer = (char*)malloc(local->recv_buffer_size); + char * buffer = out_buffer; + char error = 0; + while (local->recv_buffer_size > 4) { + memintcopy_lt(key + key_len - 4, local->recv_id); + + { + char hash[20]; + local->hmac(hash, (char*)recv_buffer, 2, key, key_len); + + if (memcmp(hash, recv_buffer + 2, 2)) { + local->recv_buffer_size = 0; + error = 1; + break; + } + } + + int length = ((int)recv_buffer[1] << 8) + recv_buffer[0]; + if (length >= 8192 || length < 8) { + local->recv_buffer_size = 0; + error = 1; + break; + } + if (length > local->recv_buffer_size) + break; + + { + char hash[20]; + local->hmac(hash, (char *)recv_buffer, length - 4, key, key_len); + if (memcmp(hash, recv_buffer + length - 4, 4)) + { + local->recv_buffer_size = 0; + error = 1; + break; + } + } + + ++local->recv_id; + int pos = recv_buffer[4]; + if (pos < 255) + { + pos += 4; + } + else + { + pos = (((int)recv_buffer[6] << 8) | recv_buffer[5]) + 4; + } + int data_size = length - pos - 4; + memmove(buffer, recv_buffer + pos, data_size); + buffer += data_size; + memmove(recv_buffer, recv_buffer + length, local->recv_buffer_size -= length); + } + int len; + if (error == 0) { + len = buffer - out_buffer; + if (*capacity < len) { + *pplaindata = (char*)realloc(*pplaindata, *capacity = len * 2); + plaindata = *pplaindata; + } + memmove(plaindata, out_buffer, len); + } else { + len = -1; + } + free(out_buffer); + free(key); + return len; +} + +int auth_aes128_sha1_client_udp_pre_encrypt(obfs *self, char **pplaindata, int datalength, size_t* capacity) { + char *plaindata = *pplaindata; + auth_simple_local_data *local = (auth_simple_local_data*)self->l_data; + char * out_buffer = (char*)malloc(datalength + 8); + + if (local->user_key == NULL) { + if(self->server.param != NULL && self->server.param[0] != 0) { + char *param = self->server.param; + char *delim = strchr(param, ':'); + if(delim != NULL) { + char uid_str[16] = {}; + strncpy(uid_str, param, delim - param); + char key_str[128]; + strcpy(key_str, delim + 1); + long uid_long = strtol(uid_str, NULL, 10); + memintcopy_lt(local->uid, uid_long); + + char hash[21] = {0}; + local->hash(hash, key_str, strlen(key_str)); + + local->user_key_len = local->hash_len; + local->user_key = (uint8_t*)malloc(local->user_key_len); + memcpy(local->user_key, hash, local->hash_len); + } + } + if (local->user_key == NULL) { + rand_bytes((uint8_t *)local->uid, 4); + + local->user_key_len = self->server.key_len; + local->user_key = (uint8_t*)malloc(local->user_key_len); + memcpy(local->user_key, self->server.key, local->user_key_len); + } + } + + int outlength = datalength + 8; + memmove(out_buffer, plaindata, datalength); + memmove(out_buffer + datalength, local->uid, 4); + + { + char hash[20]; + local->hmac(hash, out_buffer, outlength - 4, local->user_key, local->user_key_len); + memmove(out_buffer + outlength - 4, hash, 4); + } + + if (*capacity < outlength) { + *pplaindata = (char*)realloc(*pplaindata, *capacity = outlength * 2); + plaindata = *pplaindata; + } + memmove(plaindata, out_buffer, outlength); + + free(out_buffer); + return outlength; +} + +int auth_aes128_sha1_client_udp_post_decrypt(obfs *self, char **pplaindata, int datalength, size_t* capacity) { + if (datalength <= 4) + return 0; + + char *plaindata = *pplaindata; + auth_simple_local_data *local = (auth_simple_local_data*)self->l_data; + + char hash[20]; + local->hmac(hash, plaindata, datalength - 4, self->server.key, self->server.key_len); + + if (memcmp(hash, plaindata + datalength - 4, 4)) + { + return 0; + } + + return datalength - 4; +} diff --git a/shadowsocksr-libev/src/server/auth.h b/shadowsocksr-libev/src/server/auth.h new file mode 100644 index 000000000..f7730df5d --- /dev/null +++ b/shadowsocksr-libev/src/server/auth.h @@ -0,0 +1,30 @@ +/* + * auth.h - Define shadowsocksR server's buffers and callbacks + * + * Copyright (C) 2015 - 2016, Break Wa11 + */ + +#ifndef _AUTH_H +#define _AUTH_H + +void * auth_simple_init_data(); +obfs * auth_simple_new_obfs(); +void auth_simple_dispose(obfs *self); + +int auth_simple_client_pre_encrypt(obfs *self, char **pplaindata, int datalength, size_t* capacity); +int auth_simple_client_post_decrypt(obfs *self, char **pplaindata, int datalength, size_t* capacity); + + +int auth_sha1_client_pre_encrypt(obfs *self, char **pplaindata, int datalength, size_t* capacity); +int auth_sha1_client_post_decrypt(obfs *self, char **pplaindata, int datalength, size_t* capacity); + +int auth_sha1_v2_client_pre_encrypt(obfs *self, char **pplaindata, int datalength, size_t* capacity); +int auth_sha1_v2_client_post_decrypt(obfs *self, char **pplaindata, int datalength, size_t* capacity); + +int auth_sha1_v4_client_pre_encrypt(obfs *self, char **pplaindata, int datalength, size_t* capacity); +int auth_sha1_v4_client_post_decrypt(obfs *self, char **pplaindata, int datalength, size_t* capacity); + +int auth_aes128_sha1_client_pre_encrypt(obfs *self, char **pplaindata, int datalength, size_t* capacity); +int auth_aes128_sha1_client_post_decrypt(obfs *self, char **pplaindata, int datalength, size_t* capacity); + +#endif // _AUTH_H diff --git a/shadowsocksr-libev/src/server/base64.c b/shadowsocksr-libev/src/server/base64.c new file mode 100644 index 000000000..7cf9552fe --- /dev/null +++ b/shadowsocksr-libev/src/server/base64.c @@ -0,0 +1,119 @@ +#include "base64.h" + +/* BASE 64 encode table */ +static const char base64en[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +#define BASE64_PAD '=' + +#define BASE64DE_FIRST '+' +#define BASE64DE_LAST 'z' + +/* ASCII order for BASE 64 decode, -1 in unused character */ +static const signed char base64de[] = { + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + /* '+', ',', '-', '.', '/', */ + -1, -1, -1, 62, -1, -1, -1, 63, + /* '0', '1', '2', '3', '4', '5', '6', '7', */ + 52, 53, 54, 55, 56, 57, 58, 59, + /* '8', '9', ':', ';', '<', '=', '>', '?', */ + 60, 61, -1, -1, -1, -1, -1, -1, + /* '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', */ + -1, 0, 1, 2, 3, 4, 5, 6, + /* 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', */ + 7, 8, 9, 10, 11, 12, 13, 14, + /* 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', */ + 15, 16, 17, 18, 19, 20, 21, 22, + /* 'X', 'Y', 'Z', '[', '\', ']', '^', '_', */ + 23, 24, 25, -1, -1, -1, -1, -1, + /* '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', */ + -1, 26, 27, 28, 29, 30, 31, 32, + /* 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', */ + 33, 34, 35, 36, 37, 38, 39, 40, + /* 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', */ + 41, 42, 43, 44, 45, 46, 47, 48, + /* 'x', 'y', 'z', */ + 49, 50, 51, +}; + +int +base64_encode(const unsigned char *in, unsigned int inlen, char *out) +{ + unsigned int i, j; + + for (i = j = 0; i < inlen; i++) { + int s = i % 3; /* from 6/gcd(6, 8) */ + + switch (s) { + case 0: + out[j++] = base64en[(in[i] >> 2) & 0x3F]; + continue; + case 1: + out[j++] = base64en[((in[i-1] & 0x3) << 4) + ((in[i] >> 4) & 0xF)]; + continue; + case 2: + out[j++] = base64en[((in[i-1] & 0xF) << 2) + ((in[i] >> 6) & 0x3)]; + out[j++] = base64en[in[i] & 0x3F]; + } + } + + /* move back */ + i -= 1; + + /* check the last and add padding */ + if ((i % 3) == 0) { + out[j++] = base64en[(in[i] & 0x3) << 4]; + out[j++] = BASE64_PAD; + out[j++] = BASE64_PAD; + } else if ((i % 3) == 1) { + out[j++] = base64en[(in[i] & 0xF) << 2]; + out[j++] = BASE64_PAD; + } + + return BASE64_OK; +} + +int +base64_decode(const char *in, unsigned int inlen, unsigned char *out) +{ + unsigned int i, j; + + for (i = j = 0; i < inlen; i++) { + int c; + int s = i % 4; /* from 8/gcd(6, 8) */ + + if (in[i] == '=') + return BASE64_OK; + + if (in[i] < BASE64DE_FIRST || in[i] > BASE64DE_LAST || + (c = base64de[(int)in[i]]) == -1) + return BASE64_INVALID; + + switch (s) { + case 0: + out[j] = ((unsigned int)c << 2) & 0xFF; + continue; + case 1: + out[j++] += ((unsigned int)c >> 4) & 0x3; + + /* if not last char with padding */ + if (i < (inlen - 3) || in[inlen - 2] != '=') + out[j] = ((unsigned int)c & 0xF) << 4; + continue; + case 2: + out[j++] += ((unsigned int)c >> 2) & 0xF; + + /* if not last char with padding */ + if (i < (inlen - 2) || in[inlen - 1] != '=') + out[j] = ((unsigned int)c & 0x3) << 6; + continue; + case 3: + out[j++] += (unsigned char)c; + } + } + + return BASE64_OK; +} diff --git a/shadowsocksr-libev/src/server/base64.h b/shadowsocksr-libev/src/server/base64.h new file mode 100644 index 000000000..6432ba37c --- /dev/null +++ b/shadowsocksr-libev/src/server/base64.h @@ -0,0 +1,16 @@ +#ifndef __BASE64_H__ +#define __BASE64_H__ + +enum {BASE64_OK = 0, BASE64_INVALID}; + +#define BASE64_ENCODE_OUT_SIZE(s) (((s) + 2) / 3 * 4) +#define BASE64_DECODE_OUT_SIZE(s) (((s)) / 4 * 3) + +int +base64_encode(const unsigned char *in, unsigned int inlen, char *out); + +int +base64_decode(const char *in, unsigned int inlen, unsigned char *out); + + +#endif /* __BASE64_H__ */ diff --git a/shadowsocksr-libev/src/server/cache.c b/shadowsocksr-libev/src/server/cache.c new file mode 100644 index 000000000..c1a2995b7 --- /dev/null +++ b/shadowsocksr-libev/src/server/cache.c @@ -0,0 +1,308 @@ +/* + * cache.c - Manage the connection cache for UDPRELAY + * + * Copyright (C) 2013 - 2016, Max Lv + * + * This file is part of the shadowsocks-libev. + * + * shadowsocks-libev is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * shadowsocks-libev 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with shadowsocks-libev; see the file COPYING. If not, see + * . + */ + +/* + * Original Author: Oliver Lorenz (ol), olli@olorenz.org, https://olorenz.org + * License: This is licensed under the same terms as uthash itself + */ + +#include +#include + +#include "cache.h" +#include "utils.h" + +#ifdef __MINGW32__ +#include "win32.h" +#endif + +/** Creates a new cache object + * + * @param dst + * Where the newly allocated cache object will be stored in + * + * @param capacity + * The maximum number of elements this cache object can hold + * + * @return EINVAL if dst is NULL, ENOMEM if malloc fails, 0 otherwise + */ +int +cache_create(struct cache **dst, const size_t capacity, + void (*free_cb)(void *key, void *element)) +{ + struct cache *new = NULL; + + if (!dst) { + return EINVAL; + } + + if ((new = malloc(sizeof(*new))) == NULL) { + return ENOMEM; + } + + new->max_entries = capacity; + new->entries = NULL; + new->free_cb = free_cb; + *dst = new; + return 0; +} + +/** Frees an allocated cache object + * + * @param cache + * The cache object to free + * + * @param keep_data + * Whether to free contained data or just delete references to it + * + * @return EINVAL if cache is NULL, 0 otherwise + */ +int +cache_delete(struct cache *cache, int keep_data) +{ + struct cache_entry *entry, *tmp; + + if (!cache) { + return EINVAL; + } + + if (keep_data) { + HASH_CLEAR(hh, cache->entries); + } else { + HASH_ITER(hh, cache->entries, entry, tmp){ + HASH_DEL(cache->entries, entry); + if (entry->data != NULL) { + if (cache->free_cb) { + cache->free_cb(entry->key, entry->data); + } else { + ss_free(entry->data); + } + } + ss_free(entry->key); + ss_free(entry); + } + } + + ss_free(cache); + return 0; +} + +/** Clear old cache object + * + * @param cache + * The cache object to clear + * + * @param age + * Clear only objects older than the age (sec) + * + * @return EINVAL if cache is NULL, 0 otherwise + */ +int +cache_clear(struct cache *cache, ev_tstamp age) +{ + struct cache_entry *entry, *tmp; + + if (!cache) { + return EINVAL; + } + + ev_tstamp now = ev_time(); + + HASH_ITER(hh, cache->entries, entry, tmp){ + if (now - entry->ts > age) { + HASH_DEL(cache->entries, entry); + if (entry->data != NULL) { + if (cache->free_cb) { + cache->free_cb(entry->key, entry->data); + } else { + ss_free(entry->data); + } + } + ss_free(entry->key); + ss_free(entry); + } + } + + return 0; +} + +/** Removes a cache entry + * + * @param cache + * The cache object + * + * @param key + * The key of the entry to remove + * + * @param key_len + * The length of key + * + * @return EINVAL if cache is NULL, 0 otherwise + */ +int +cache_remove(struct cache *cache, char *key, size_t key_len) +{ + struct cache_entry *tmp; + + if (!cache || !key) { + return EINVAL; + } + + HASH_FIND(hh, cache->entries, key, key_len, tmp); + + if (tmp) { + HASH_DEL(cache->entries, tmp); + if (tmp->data != NULL) { + if (cache->free_cb) { + cache->free_cb(tmp->key, tmp->data); + } else { + ss_free(tmp->data); + } + } + ss_free(tmp->key); + ss_free(tmp); + } + + return 0; +} + +/** Checks if a given key is in the cache + * + * @param cache + * The cache object + * + * @param key + * The key to look-up + * + * @param key_len + * The length of key + * + * @param result + * Where to store the result if key is found. + * + * A warning: Even though result is just a pointer, + * you have to call this function with a **ptr, + * otherwise this will blow up in your face. + * + * @return EINVAL if cache is NULL, 0 otherwise + */ +int +cache_lookup(struct cache *cache, char *key, size_t key_len, void *result) +{ + struct cache_entry *tmp = NULL; + char **dirty_hack = result; + + if (!cache || !key || !result) { + return EINVAL; + } + + HASH_FIND(hh, cache->entries, key, key_len, tmp); + if (tmp) { + HASH_DELETE(hh, cache->entries, tmp); + tmp->ts = ev_time(); + HASH_ADD_KEYPTR(hh, cache->entries, tmp->key, key_len, tmp); + *dirty_hack = tmp->data; + } else { + *dirty_hack = result = NULL; + } + + return 0; +} + +int +cache_key_exist(struct cache *cache, char *key, size_t key_len) +{ + struct cache_entry *tmp = NULL; + + if (!cache || !key) { + return 0; + } + + HASH_FIND(hh, cache->entries, key, key_len, tmp); + if (tmp) { + HASH_DELETE(hh, cache->entries, tmp); + tmp->ts = ev_time(); + HASH_ADD_KEYPTR(hh, cache->entries, tmp->key, key_len, tmp); + return 1; + } else { + return 0; + } + + return 0; +} + +/** Inserts a given pair into the cache + * + * @param cache + * The cache object + * + * @param key + * The key that identifies + * + * @param key_len + * The length of key + * + * @param data + * Data associated with + * + * @return EINVAL if cache is NULL, ENOMEM if malloc fails, 0 otherwise + */ +int +cache_insert(struct cache *cache, char *key, size_t key_len, void *data) +{ + struct cache_entry *entry = NULL; + struct cache_entry *tmp_entry = NULL; + + if (!cache) { + return EINVAL; + } + + if ((entry = malloc(sizeof(*entry))) == NULL) { + return ENOMEM; + } + + entry->key = ss_malloc(key_len + 1); + memcpy(entry->key, key, key_len); + entry->key[key_len] = 0; + + entry->data = data; + entry->ts = ev_time(); + HASH_ADD_KEYPTR(hh, cache->entries, entry->key, key_len, entry); + + if (HASH_COUNT(cache->entries) >= cache->max_entries) { + HASH_ITER(hh, cache->entries, entry, tmp_entry){ + HASH_DELETE(hh, cache->entries, entry); + if (entry->data != NULL) { + if (cache->free_cb) { + cache->free_cb(entry->key, entry->data); + } else { + ss_free(entry->data); + } + } + ss_free(entry->key); + ss_free(entry); + break; + } + } + + return 0; +} diff --git a/shadowsocksr-libev/src/server/cache.h b/shadowsocksr-libev/src/server/cache.h new file mode 100644 index 000000000..0ec98f55a --- /dev/null +++ b/shadowsocksr-libev/src/server/cache.h @@ -0,0 +1,62 @@ +/* + * cache.h - Define the cache manager interface + * + * Copyright (C) 2013 - 2016, Max Lv + * + * This file is part of the shadowsocks-libev. + * + * shadowsocks-libev is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * shadowsocks-libev 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with shadowsocks-libev; see the file COPYING. If not, see + * . + */ + +/* + * Original Author: Oliver Lorenz (ol), olli@olorenz.org, https://olorenz.org + * License: This is licensed under the same terms as uthash itself + */ + +#ifndef _CACHE_ +#define _CACHE_ + +#include "uthash.h" +#include "ev.h" + +/** + * A cache entry + */ +struct cache_entry { + char *key; /** +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//#define __DEBUG__ +#ifdef __DEBUG__ +#define DEBUG(format,...) printf("File: "__FILE__", Line: %05d: "format"/n", __LINE__, ##__VA_ARGS__) +#else +#define DEBUG(format,...) +#endif + +static sigjmp_buf jmpbuf; +static void alarm_func() +{ + siglongjmp(jmpbuf, 1); +} + +static struct hostent *timeGethostbyname(const char *domain, int timeout) +{ + struct hostent *ipHostent = NULL; + signal(SIGALRM, alarm_func); + if(sigsetjmp(jmpbuf, 1) != 0) + { + alarm(0);//timout + signal(SIGALRM, SIG_IGN); + return NULL; + } + alarm(timeout);//setting alarm + ipHostent = gethostbyname(domain); + signal(SIGALRM, SIG_IGN); + return ipHostent; +} + + +#define MY_HTTP_DEFAULT_PORT 80 +#define BUFFER_SIZE 1024 +#define HTTP_POST "POST /%s HTTP/1.1\r\nHOST: %s:%d\r\nAccept: */*\r\n"\ + "Content-Type:application/x-www-form-urlencoded\r\nContent-Length: %d\r\n\r\n%s" +#define HTTP_GET "GET /%s HTTP/1.1\r\nHOST: %s:%d\r\nAccept: */*\r\n\r\n" + +static int http_parse_url(const char *url,char *host,char *file,int *port) +{ + char *ptr1,*ptr2; + int len = 0; + if(!url || !host || !file || !port){ + return 1; + } + + ptr1 = (char *)url; + + if(!strncmp(ptr1,"http://",strlen("http://"))){ + ptr1 += strlen("http://"); + }else{ + return 1; + } + + ptr2 = strchr(ptr1,'/'); + if(ptr2){ + len = strlen(ptr1) - strlen(ptr2); + memcpy(host,ptr1,len); + host[len] = '\0'; + if(*(ptr2 + 1)){ + memcpy(file,ptr2 + 1,strlen(ptr2) - 1 ); + file[strlen(ptr2) - 1] = '\0'; + } + }else{ + memcpy(host,ptr1,strlen(ptr1)); + host[strlen(ptr1)] = '\0'; + } + //get host and ip + ptr1 = strchr(host,':'); + if(ptr1){ + *ptr1++ = '\0'; + *port = atoi(ptr1); + }else{ + *port = MY_HTTP_DEFAULT_PORT; + } + + return 0; +} + + +static int http_tcpclient_recv(int socket,char *lpbuff){ + int recvnum = 0; + + recvnum = recv(socket, lpbuff,BUFFER_SIZE*4,0); + + return recvnum; +} + +static int http_tcpclient_send(int socket,char *buff,int size){ + int sent=0,tmpres=0; + + while(sent < size){ + tmpres = send(socket,buff+sent,size-sent,0); + if(tmpres == -1){ + return 1; + } + sent += tmpres; + } + return sent; +} + + + + + +int http_get(const char *url,int socket_fd) +{ + char lpbuf[BUFFER_SIZE*4] = {'\0'}; + + char host_addr[BUFFER_SIZE] = {'\0'}; + char file[BUFFER_SIZE] = {'\0'}; + int port = 0; + + + if(!url){ + DEBUG(" failed!\n"); + return 1; + } + + if(http_parse_url(url,host_addr,file,&port)){ + DEBUG("http_parse_url failed!\n"); + return 1; + } + DEBUG("url: %s\thost_addr : %s\tfile:%s\t,%d\n",url,host_addr,file,port); + + + if(socket_fd < 0){ + DEBUG("http_tcpclient_create failed\n"); + return 1; + } + + sprintf(lpbuf,HTTP_GET,file,host_addr,port); + + if(http_tcpclient_send(socket_fd,lpbuf,strlen(lpbuf)) < 0){ + DEBUG("http_tcpclient_send failed..\n"); + return 1; + } + DEBUG("request:\n%s\n",lpbuf); + + if(http_tcpclient_recv(socket_fd,lpbuf) <= 0){ + DEBUG("http_tcpclient_recv failed\n"); + close(socket_fd); + return 1; + } + DEBUG("rec:\n%s\n",lpbuf); + close(socket_fd); + + //return http_parse_result(lpbuf); +return 0; +} + + + +int main(int argc, char *argv[]) +{ + int fd,http_flag=0,http_ret=1; + struct sockaddr_in addr; + struct hostent *host; + struct timeval timeo = {3, 0}; + socklen_t len = sizeof(timeo); + + char http_url[100]="http://"; + + + + fd = socket(AF_INET, SOCK_STREAM, 0); + if (argc >= 4) + timeo.tv_sec = atoi(argv[3]); + if (argc>=5) + http_flag=1; + + if((host=timeGethostbyname(argv[1],timeo.tv_sec)) == NULL) { + DEBUG("gethostbyname err\n"); + return 1; + } + if (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &timeo, len) == -1) + { + + DEBUG("setsockopt send err\n"); + return 1; + } + + if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeo, len) == -1) + { + + DEBUG("setsockopt recv err\n"); + return 1; + } + + addr.sin_family = AF_INET; + addr.sin_addr = *((struct in_addr *)host->h_addr); + //addr.sin_addr.s_addr = inet_addr(argv[1]); + addr.sin_port = htons(atoi(argv[2])); +if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) + { + if (errno == EINPROGRESS) + { + DEBUG("timeout err\n"); + return 1; + } + DEBUG("connect err\n"); + return 1; + } +if(http_flag==0) +{ + close(fd); + return 0; +} +strcat(http_url,argv[1]); +http_ret=http_get(http_url,fd); +if(http_ret==1) +{ +DEBUG("recv err"); + return 1; +} +else +{ +DEBUG("recv ok"); + + return 0; +} + +} \ No newline at end of file diff --git a/shadowsocksr-libev/src/server/common.h b/shadowsocksr-libev/src/server/common.h new file mode 100644 index 000000000..000f084ec --- /dev/null +++ b/shadowsocksr-libev/src/server/common.h @@ -0,0 +1,58 @@ +/* + * common.h - Provide global definitions + * + * Copyright (C) 2013 - 2016, Max Lv + * + * This file is part of the shadowsocks-libev. + * shadowsocks-libev is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * shadowsocks-libev 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with shadowsocks-libev; see the file COPYING. If not, see + * . + */ + +#ifndef _COMMON_H +#define _COMMON_H + +#define DEFAULT_CONF_PATH "/etc/shadowsocks-libev/config.json" + +#ifndef SOL_TCP +#define SOL_TCP IPPROTO_TCP +#endif + +#if defined(MODULE_TUNNEL) || defined(MODULE_REDIR) +#define MODULE_LOCAL +#endif + +int init_udprelay(const char *server_host, const char *server_port, +#ifdef MODULE_LOCAL + const struct sockaddr *remote_addr, const int remote_addr_len, +#ifdef MODULE_TUNNEL + const ss_addr_t tunnel_addr, +#endif +#endif + int mtu, int method, int auth, int timeout, const char *iface, const char *protocol, const char *protocol_param); + +void free_udprelay(void); + +#ifdef ANDROID +int protect_socket(int fd); +int send_traffic_stat(uint64_t tx, uint64_t rx); +#endif + +#define STAGE_ERROR -1 /* Error detected */ +#define STAGE_INIT 0 /* Initial stage */ +#define STAGE_HANDSHAKE 1 /* Handshake with client */ +#define STAGE_PARSE 2 /* Parse the header */ +#define STAGE_RESOLVE 4 /* Resolve the hostname */ +#define STAGE_STREAM 5 /* Stream between client and server */ + +#endif // _COMMON_H diff --git a/shadowsocksr-libev/src/server/crc32.c b/shadowsocksr-libev/src/server/crc32.c new file mode 100644 index 000000000..6d328d231 --- /dev/null +++ b/shadowsocksr-libev/src/server/crc32.c @@ -0,0 +1,97 @@ +static uint32_t crc32_table[256] = {0}; + +void init_crc32_table(void) { + uint32_t c, i, j; + if (crc32_table[0] == 0) { + for (i = 0; i < 256; i++) { + c = i; + for (j = 0; j < 8; j++) { + if (c & 1) + c = 0xedb88320L ^ (c >> 1); + else + c = c >> 1; + } + crc32_table[i] = c; + } + } +} + +uint32_t crc32(unsigned char *buffer, unsigned int size) { + uint32_t crc = 0xFFFFFFFF; + unsigned int i; + for (i = 0; i < size; i++) { + crc = crc32_table[(crc ^ buffer[i]) & 0xFF] ^ (crc >> 8); + } + return crc ^ 0xFFFFFFFF; +} + +void fillcrc32to(unsigned char *buffer, unsigned int size, unsigned char *outbuffer) { + uint32_t crc = 0xFFFFFFFF; + unsigned int i; + for (i = 0; i < size; i++) { + crc = crc32_table[(crc ^ buffer[i]) & 0xff] ^ (crc >> 8); + } + crc ^= 0xFFFFFFFF; + outbuffer[0] = crc; + outbuffer[1] = crc >> 8; + outbuffer[2] = crc >> 16; + outbuffer[3] = crc >> 24; +} + +void fillcrc32(unsigned char *buffer, unsigned int size) { + uint32_t crc = 0xFFFFFFFF; + unsigned int i; + size -= 4; + for (i = 0; i < size; i++) { + crc = crc32_table[(crc ^ buffer[i]) & 0xff] ^ (crc >> 8); + } + buffer += size; + buffer[0] = crc; + buffer[1] = crc >> 8; + buffer[2] = crc >> 16; + buffer[3] = crc >> 24; +} + +void adler32_short(unsigned char *buffer, unsigned int size, uint32_t *a, uint32_t *b) { + for (int i = 0; i < size; i++) { + *a += buffer[i]; + *b += *a; + } + *a %= 65521; + *b %= 65521; +} + +#define NMAX 5552 +uint32_t adler32(unsigned char *buffer, unsigned int size) { + uint32_t a = 1; + uint32_t b = 0; + while ( size >= NMAX ) { + adler32_short(buffer, NMAX, &a, &b); + buffer += NMAX; + size -= NMAX; + } + adler32_short(buffer, size, &a, &b); + return (b << 16) + a; +} +#undef NMAX + +void filladler32(unsigned char *buffer, unsigned int size) { + size -= 4; + uint32_t checksum = adler32(buffer, size); + buffer += size; + buffer[0] = checksum; + buffer[1] = checksum >> 8; + buffer[2] = checksum >> 16; + buffer[3] = checksum >> 24; +} + +int checkadler32(unsigned char *buffer, unsigned int size) { + size -= 4; + uint32_t checksum = adler32(buffer, size); + buffer += size; + return checksum == (((uint32_t)buffer[3] << 24) + | ((uint32_t)buffer[2] << 16) + | ((uint32_t)buffer[1] << 8) + | (uint32_t)buffer[0]); +} + diff --git a/shadowsocksr-libev/src/server/encrypt.c b/shadowsocksr-libev/src/server/encrypt.c new file mode 100644 index 000000000..37dd5cdf0 --- /dev/null +++ b/shadowsocksr-libev/src/server/encrypt.c @@ -0,0 +1,1645 @@ +/* + * encrypt.c - Manage the global encryptor + * + * Copyright (C) 2013 - 2016, Max Lv + * + * This file is part of the shadowsocks-libev. + * + * shadowsocks-libev is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * shadowsocks-libev 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with shadowsocks-libev; see the file COPYING. If not, see + * . + */ + +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#if defined(USE_CRYPTO_OPENSSL) + +#include +#include +#include +#include + +#elif defined(USE_CRYPTO_POLARSSL) + +#include +#include +#include +#include +#include +#include +#define CIPHER_UNSUPPORTED "unsupported" + +#include +#ifdef _WIN32 +#include +#include +#else +#include +#endif + +#elif defined(USE_CRYPTO_MBEDTLS) + +#include +#include +#include +#include +#include +#define CIPHER_UNSUPPORTED "unsupported" + +#include +#ifdef _WIN32 +#include +#include +#else +#include +#endif + +#endif + +#include + +#ifndef __MINGW32__ +#include +#endif + +#include "cache.h" +#include "encrypt.h" +#include "utils.h" + +#define OFFSET_ROL(p, o) ((uint64_t)(*(p + o)) << (8 * o)) + +static uint8_t *enc_table; +static uint8_t *dec_table; +static uint8_t enc_key[MAX_KEY_LENGTH]; +static int enc_key_len; +static int enc_iv_len; +static int enc_method; + +static struct cache *iv_cache; + +#ifdef DEBUG +static void +dump(char *tag, char *text, int len) +{ + int i; + printf("%s: ", tag); + for (i = 0; i < len; i++) + printf("0x%02x ", (uint8_t)text[i]); + printf("\n"); +} + +#endif + +static const char *supported_ciphers[CIPHER_NUM] = { + "table", + "rc4", + "rc4-md5-6", + "rc4-md5", + "aes-128-cfb", + "aes-192-cfb", + "aes-256-cfb", + "aes-128-ctr", + "aes-192-ctr", + "aes-256-ctr", + "bf-cfb", + "camellia-128-cfb", + "camellia-192-cfb", + "camellia-256-cfb", + "cast5-cfb", + "des-cfb", + "idea-cfb", + "rc2-cfb", + "seed-cfb", + "salsa20", + "chacha20", + "chacha20-ietf" +}; + +#ifdef USE_CRYPTO_POLARSSL +static const char *supported_ciphers_polarssl[CIPHER_NUM] = { + "table", + "ARC4-128", + "ARC4-128", + "ARC4-128", + "AES-128-CFB128", + "AES-192-CFB128", + "AES-256-CFB128", + "AES-128-CTR", + "AES-192-CTR", + "AES-256-CTR", + "BLOWFISH-CFB64", + "CAMELLIA-128-CFB128", + "CAMELLIA-192-CFB128", + "CAMELLIA-256-CFB128", + CIPHER_UNSUPPORTED, + CIPHER_UNSUPPORTED, + CIPHER_UNSUPPORTED, + CIPHER_UNSUPPORTED, + CIPHER_UNSUPPORTED, + "salsa20", + "chacha20", + "chacha20-ietf" +}; +#endif + +#ifdef USE_CRYPTO_MBEDTLS +static const char *supported_ciphers_mbedtls[CIPHER_NUM] = { + "table", + "ARC4-128", + "ARC4-128", + "ARC4-128", + "AES-128-CFB128", + "AES-192-CFB128", + "AES-256-CFB128", + "AES-128-CTR", + "AES-192-CTR", + "AES-256-CTR", + "BLOWFISH-CFB64", + "CAMELLIA-128-CFB128", + "CAMELLIA-192-CFB128", + "CAMELLIA-256-CFB128", + CIPHER_UNSUPPORTED, + CIPHER_UNSUPPORTED, + CIPHER_UNSUPPORTED, + CIPHER_UNSUPPORTED, + CIPHER_UNSUPPORTED, + "salsa20", + "chacha20", + "chacha20-ietf" +}; +#endif + +#ifdef USE_CRYPTO_APPLECC +static const CCAlgorithm supported_ciphers_applecc[CIPHER_NUM] = { + kCCAlgorithmInvalid, + kCCAlgorithmRC4, + kCCAlgorithmRC4, + kCCAlgorithmRC4, + kCCAlgorithmAES, + kCCAlgorithmAES, + kCCAlgorithmAES, + kCCAlgorithmAES, + kCCAlgorithmAES, + kCCAlgorithmAES, + kCCAlgorithmBlowfish, + kCCAlgorithmInvalid, + kCCAlgorithmInvalid, + kCCAlgorithmInvalid, + kCCAlgorithmCAST, + kCCAlgorithmDES, + kCCAlgorithmInvalid, + kCCAlgorithmRC2, + kCCAlgorithmInvalid, + kCCAlgorithmInvalid, + kCCAlgorithmInvalid, + kCCAlgorithmInvalid +}; + +static const CCMode supported_modes_applecc[CIPHER_NUM] = { + kCCAlgorithmInvalid, + kCCAlgorithmInvalid, + kCCModeRC4, + kCCModeRC4, + kCCModeCFB, + kCCModeCFB, + kCCModeCFB, + kCCModeCTR, + kCCModeCTR, + kCCModeCTR, + kCCModeCFB, + kCCAlgorithmInvalid, + kCCAlgorithmInvalid, + kCCAlgorithmInvalid, + kCCModeCFB, + kCCModeCFB, + kCCModeCFB, + kCCModeCFB, + kCCAlgorithmInvalid, + kCCAlgorithmInvalid, + kCCAlgorithmInvalid, + kCCAlgorithmInvalid +}; +#endif + +static const int supported_ciphers_iv_size[CIPHER_NUM] = { + 0, 0, 6, 16, 16, 16, 16, 16, 16, 16, 8, 16, 16, 16, 8, 8, 8, 8, 16, 8, 8, 12 +}; + +static const int supported_ciphers_key_size[CIPHER_NUM] = { + 0, 16, 16, 16, 16, 24, 32, 16, 24, 32, 16, 16, 24, 32, 16, 8, 16, 16, 16, 32, 32, 32 +}; + +static int +safe_memcmp(const void *s1, const void *s2, size_t n) +{ + const unsigned char *_s1 = (const unsigned char *)s1; + const unsigned char *_s2 = (const unsigned char *)s2; + int ret = 0; + size_t i; + for (i = 0; i < n; i++) + ret |= _s1[i] ^ _s2[i]; + return !!ret; +} + +int +balloc(buffer_t *ptr, size_t capacity) +{ + sodium_memzero(ptr, sizeof(buffer_t)); + ptr->array = ss_malloc(capacity); + ptr->capacity = capacity; + return capacity; +} + +int +brealloc(buffer_t *ptr, size_t len, size_t capacity) +{ + if (ptr == NULL) + return -1; + size_t real_capacity = max(len, capacity); + if (ptr->capacity < real_capacity) { + ptr->array = ss_realloc(ptr->array, real_capacity); + ptr->capacity = real_capacity; + } + return real_capacity; +} + +void +bfree(buffer_t *ptr) +{ + if (ptr == NULL) + return; + ptr->idx = 0; + ptr->len = 0; + ptr->capacity = 0; + if (ptr->array != NULL) { + ss_free(ptr->array); + } +} + +static int +crypto_stream_xor_ic(uint8_t *c, const uint8_t *m, uint64_t mlen, + const uint8_t *n, uint64_t ic, const uint8_t *k, + int method) +{ + switch (method) { + case SALSA20: + return crypto_stream_salsa20_xor_ic(c, m, mlen, n, ic, k); + case CHACHA20: + return crypto_stream_chacha20_xor_ic(c, m, mlen, n, ic, k); + case CHACHA20IETF: + return crypto_stream_chacha20_ietf_xor_ic(c, m, mlen, n, (uint32_t)ic, k); + } + // always return 0 + return 0; +} + +static int +random_compare(const void *_x, const void *_y, uint32_t i, + uint64_t a) +{ + uint8_t x = *((uint8_t *)_x); + uint8_t y = *((uint8_t *)_y); + return a % (x + i) - a % (y + i); +} + +static void +merge(uint8_t *left, int llength, uint8_t *right, + int rlength, uint32_t salt, uint64_t key) +{ + uint8_t *ltmp = (uint8_t *)malloc(llength * sizeof(uint8_t)); + uint8_t *rtmp = (uint8_t *)malloc(rlength * sizeof(uint8_t)); + + uint8_t *ll = ltmp; + uint8_t *rr = rtmp; + + uint8_t *result = left; + + memcpy(ltmp, left, llength * sizeof(uint8_t)); + memcpy(rtmp, right, rlength * sizeof(uint8_t)); + + while (llength > 0 && rlength > 0) { + if (random_compare(ll, rr, salt, key) <= 0) { + *result = *ll; + ++ll; + --llength; + } else { + *result = *rr; + ++rr; + --rlength; + } + ++result; + } + + if (llength > 0) { + while (llength > 0) { + *result = *ll; + ++result; + ++ll; + --llength; + } + } else { + while (rlength > 0) { + *result = *rr; + ++result; + ++rr; + --rlength; + } + } + + ss_free(ltmp); + ss_free(rtmp); +} + +static void +merge_sort(uint8_t array[], int length, + uint32_t salt, uint64_t key) +{ + uint8_t middle; + uint8_t *left, *right; + int llength; + + if (length <= 1) { + return; + } + + middle = length / 2; + + llength = length - middle; + + left = array; + right = array + llength; + + merge_sort(left, llength, salt, key); + merge_sort(right, middle, salt, key); + merge(left, llength, right, middle, salt, key); +} + +int +enc_get_iv_len() +{ + return enc_iv_len; +} + +uint8_t* enc_get_key() +{ + return enc_key; +} + +int enc_get_key_len() +{ + return enc_key_len; +} + +unsigned char *enc_md5(const unsigned char *d, size_t n, unsigned char *md) +{ +#if defined(USE_CRYPTO_OPENSSL) + return MD5(d, n, md); +#elif defined(USE_CRYPTO_POLARSSL) + static unsigned char m[16]; + if (md == NULL) { + md = m; + } + md5(d, n, md); + return md; +#elif defined(USE_CRYPTO_MBEDTLS) + static unsigned char m[16]; + if (md == NULL) { + md = m; + } + mbedtls_md5(d, n, md); + return md; +#endif +} + +void +enc_table_init(const char *pass) +{ + uint32_t i; + uint64_t key = 0; + uint8_t *digest; + + enc_table = ss_malloc(256); + dec_table = ss_malloc(256); + + digest = enc_md5((const uint8_t *)pass, strlen(pass), NULL); + + for (i = 0; i < 8; i++) + key += OFFSET_ROL(digest, i); + + for (i = 0; i < 256; ++i) + enc_table[i] = i; + for (i = 1; i < 1024; ++i) + merge_sort(enc_table, 256, i, key); + for (i = 0; i < 256; ++i) + // gen decrypt table from encrypt table + dec_table[enc_table[i]] = i; +} + +int +cipher_iv_size(const cipher_t *cipher) +{ +#if defined(USE_CRYPTO_OPENSSL) + if (cipher->info == NULL) + return cipher->iv_len; + else + return EVP_CIPHER_iv_length(cipher->info); +#elif defined(USE_CRYPTO_POLARSSL) || defined(USE_CRYPTO_MBEDTLS) + if (cipher == NULL) { + return 0; + } + return cipher->info->iv_size; +#endif +} + +int +cipher_key_size(const cipher_t *cipher) +{ +#if defined(USE_CRYPTO_OPENSSL) + if (cipher->info == NULL) + return cipher->key_len; + else + return EVP_CIPHER_key_length(cipher->info); +#elif defined(USE_CRYPTO_POLARSSL) + if (cipher == NULL) { + return 0; + } + /* Override PolarSSL 32 bit default key size with sane 128 bit default */ + if (cipher->info->base != NULL && POLARSSL_CIPHER_ID_BLOWFISH == + cipher->info->base->cipher) { + return 128 / 8; + } + return cipher->info->key_length / 8; +#elif defined(USE_CRYPTO_MBEDTLS) + /* + * Semi-API changes (technically public, morally private) + * Renamed a few headers to include _internal in the name. Those headers are + * not supposed to be included by users. + * Changed md_info_t into an opaque structure (use md_get_xxx() accessors). + * Changed pk_info_t into an opaque structure. + * Changed cipher_base_t into an opaque structure. + */ + if (cipher == NULL) { + return 0; + } + /* From Version 1.2.7 released 2013-04-13 Default Blowfish keysize is now 128-bits */ + return cipher->info->key_bitlen / 8; +#endif +} + +void +bytes_to_key_with_size(const char *pass, size_t len, uint8_t *md, size_t md_size) +{ + uint8_t result[128]; + enc_md5((const unsigned char *)pass, len, result); + memcpy(md, result, 16); + int i = 16; + for (; i < md_size; i += 16) { + memcpy(result + 16, pass, len); + enc_md5(result, 16 + len, result); + memcpy(md + i, result, 16); + } +} + +int +bytes_to_key(const cipher_t *cipher, const digest_type_t *md, + const uint8_t *pass, uint8_t *key) +{ + size_t datal; + datal = strlen((const char *)pass); + +#if defined(USE_CRYPTO_OPENSSL) + + MD5_CTX c; + unsigned char md_buf[MAX_MD_SIZE]; + int nkey; + int addmd; + unsigned int i, j, mds; + + mds = 16; + nkey = cipher_key_size(cipher); + if (pass == NULL) + return nkey; + memset(&c, 0, sizeof(MD5_CTX)); + + for (j = 0, addmd = 0; j < nkey; addmd++) { + MD5_Init(&c); + if (addmd) { + MD5_Update(&c, md_buf, mds); + } + MD5_Update(&c, pass, datal); + MD5_Final(md_buf, &c); + + for (i = 0; i < mds; i++, j++) { + if (j >= nkey) + break; + key[j] = md_buf[i]; + } + } + + return nkey; + +#elif defined(USE_CRYPTO_POLARSSL) + md_context_t c; + unsigned char md_buf[MAX_MD_SIZE]; + int nkey; + int addmd; + unsigned int i, j, mds; + + nkey = cipher_key_size(cipher); + mds = md_get_size(md); + memset(&c, 0, sizeof(md_context_t)); + + if (pass == NULL) + return nkey; + if (md_init_ctx(&c, md)) + return 0; + + for (j = 0, addmd = 0; j < nkey; addmd++) { + md_starts(&c); + if (addmd) { + md_update(&c, md_buf, mds); + } + md_update(&c, pass, datal); + md_finish(&c, md_buf); + + for (i = 0; i < mds; i++, j++) { + if (j >= nkey) + break; + key[j] = md_buf[i]; + } + } + + md_free_ctx(&c); + return nkey; + +#elif defined(USE_CRYPTO_MBEDTLS) + + mbedtls_md_context_t c; + unsigned char md_buf[MAX_MD_SIZE]; + int nkey; + int addmd; + unsigned int i, j, mds; + + nkey = cipher_key_size(cipher); + mds = mbedtls_md_get_size(md); + memset(&c, 0, sizeof(mbedtls_md_context_t)); + + if (pass == NULL) + return nkey; + if (mbedtls_md_setup(&c, md, 1)) + return 0; + + for (j = 0, addmd = 0; j < nkey; addmd++) { + mbedtls_md_starts(&c); + if (addmd) { + mbedtls_md_update(&c, md_buf, mds); + } + mbedtls_md_update(&c, pass, datal); + mbedtls_md_finish(&c, &(md_buf[0])); + + for (i = 0; i < mds; i++, j++) { + if (j >= nkey) + break; + key[j] = md_buf[i]; + } + } + + mbedtls_md_free(&c); + return nkey; +#endif +} + +int +rand_bytes(uint8_t *output, int len) +{ + randombytes_buf(output, len); + // always return success + return 0; +} + +const cipher_kt_t * +get_cipher_type(int method) +{ + if (method <= TABLE || method >= CIPHER_NUM) { + LOGE("get_cipher_type(): Illegal method"); + return NULL; + } + + if (method == RC4_MD5 || method == RC4_MD5_6) { + method = RC4; + } + + if (method >= SALSA20) { + return NULL; + } + + const char *ciphername = supported_ciphers[method]; +#if defined(USE_CRYPTO_OPENSSL) + return EVP_get_cipherbyname(ciphername); +#elif defined(USE_CRYPTO_POLARSSL) + const char *polarname = supported_ciphers_polarssl[method]; + if (strcmp(polarname, CIPHER_UNSUPPORTED) == 0) { + LOGE("Cipher %s currently is not supported by PolarSSL library", + ciphername); + return NULL; + } + return cipher_info_from_string(polarname); +#elif defined(USE_CRYPTO_MBEDTLS) + const char *mbedtlsname = supported_ciphers_mbedtls[method]; + if (strcmp(mbedtlsname, CIPHER_UNSUPPORTED) == 0) { + LOGE("Cipher %s currently is not supported by mbed TLS library", + ciphername); + return NULL; + } + return mbedtls_cipher_info_from_string(mbedtlsname); +#endif +} + +const digest_type_t * +get_digest_type(const char *digest) +{ + if (digest == NULL) { + LOGE("get_digest_type(): Digest name is null"); + return NULL; + } + +#if defined(USE_CRYPTO_OPENSSL) + return EVP_get_digestbyname(digest); +#elif defined(USE_CRYPTO_POLARSSL) + return md_info_from_string(digest); +#elif defined(USE_CRYPTO_MBEDTLS) + return mbedtls_md_info_from_string(digest); +#endif +} + +void +cipher_context_init(cipher_ctx_t *ctx, int method, int enc) +{ + if (method <= TABLE || method >= CIPHER_NUM) { + LOGE("cipher_context_init(): Illegal method"); + return; + } + + if (method >= SALSA20) { + enc_iv_len = supported_ciphers_iv_size[method]; + return; + } + + const char *ciphername = supported_ciphers[method]; +#if defined(USE_CRYPTO_APPLECC) + cipher_cc_t *cc = &ctx->cc; + cc->cryptor = NULL; + cc->cipher = supported_ciphers_applecc[method]; + if (cc->cipher == kCCAlgorithmInvalid) { + cc->valid = kCCContextInvalid; + } else { + cc->valid = kCCContextValid; + if (cc->cipher == kCCAlgorithmRC4) { + cc->mode = supported_modes_applecc[method]; + cc->padding = ccNoPadding; + } else { + cc->mode = supported_modes_applecc[method]; + if (cc->mode == kCCModeCTR) { + cc->padding = ccNoPadding; + } else { + cc->padding = ccPKCS7Padding; + } + } + return; + } +#endif + + const cipher_kt_t *cipher = get_cipher_type(method); + +#if defined(USE_CRYPTO_OPENSSL) + ctx->evp = EVP_CIPHER_CTX_new(); + cipher_evp_t *evp = ctx->evp; + + if (cipher == NULL) { + LOGE("Cipher %s not found in OpenSSL library", ciphername); + FATAL("Cannot initialize cipher"); + } + if (!EVP_CipherInit_ex(evp, cipher, NULL, NULL, NULL, enc)) { + LOGE("Cannot initialize cipher %s", ciphername); + exit(EXIT_FAILURE); + } + if (!EVP_CIPHER_CTX_set_key_length(evp, enc_key_len)) { + EVP_CIPHER_CTX_cleanup(evp); + LOGE("Invalid key length: %d", enc_key_len); + exit(EXIT_FAILURE); + } + if (method > RC4_MD5) { + EVP_CIPHER_CTX_set_padding(evp, 1); + } +#elif defined(USE_CRYPTO_POLARSSL) + ctx->evp = (cipher_evp_t *)ss_malloc(sizeof(cipher_evp_t)); + cipher_evp_t *evp = ctx->evp; + + if (cipher == NULL) { + LOGE("Cipher %s not found in PolarSSL library", ciphername); + FATAL("Cannot initialize PolarSSL cipher"); + } + if (cipher_init_ctx(evp, cipher) != 0) { + FATAL("Cannot initialize PolarSSL cipher context"); + } +#elif defined(USE_CRYPTO_MBEDTLS) + ctx->evp = (cipher_evp_t *)ss_malloc(sizeof(cipher_evp_t)); + cipher_evp_t *evp = ctx->evp; + + if (cipher == NULL) { + LOGE("Cipher %s not found in mbed TLS library", ciphername); + FATAL("Cannot initialize mbed TLS cipher"); + } + mbedtls_cipher_init(evp); + if (mbedtls_cipher_setup(evp, cipher) != 0) { + FATAL("Cannot initialize mbed TLS cipher context"); + } +#endif +} + +void +cipher_context_set_iv(cipher_ctx_t *ctx, uint8_t *iv, size_t iv_len, + int enc) +{ + const unsigned char *true_key; + + if (iv == NULL) { + LOGE("cipher_context_set_iv(): IV is null"); + return; + } + + if (!enc) { + memcpy(ctx->iv, iv, iv_len); + } + + if (enc_method >= SALSA20) { + return; + } + + if (enc_method == RC4_MD5 || enc_method == RC4_MD5_6) { + unsigned char key_iv[32]; + memcpy(key_iv, enc_key, 16); + memcpy(key_iv + 16, iv, iv_len); + true_key = enc_md5(key_iv, 16 + iv_len, NULL); + iv_len = 0; + } else { + true_key = enc_key; + } + +#ifdef USE_CRYPTO_APPLECC + cipher_cc_t *cc = &ctx->cc; + if (cc->valid == kCCContextValid) { + memcpy(cc->iv, iv, iv_len); + memcpy(cc->key, true_key, enc_key_len); + cc->iv_len = iv_len; + cc->key_len = enc_key_len; + cc->encrypt = enc ? kCCEncrypt : kCCDecrypt; + if (cc->cryptor != NULL) { + CCCryptorRelease(cc->cryptor); + cc->cryptor = NULL; + } + + CCCryptorStatus ret; + ret = CCCryptorCreateWithMode( + cc->encrypt, + cc->mode, + cc->cipher, + cc->padding, + cc->iv, cc->key, cc->key_len, + NULL, 0, 0, kCCModeOptionCTR_BE, + &cc->cryptor); + if (ret != kCCSuccess) { + if (cc->cryptor != NULL) { + CCCryptorRelease(cc->cryptor); + cc->cryptor = NULL; + } + FATAL("Cannot set CommonCrypto key and IV"); + } + return; + } +#endif + + cipher_evp_t *evp = ctx->evp; + if (evp == NULL) { + LOGE("cipher_context_set_iv(): Cipher context is null"); + return; + } +#if defined(USE_CRYPTO_OPENSSL) + if (!EVP_CipherInit_ex(evp, NULL, NULL, true_key, iv, enc)) { + EVP_CIPHER_CTX_cleanup(evp); + FATAL("Cannot set key and IV"); + } +#elif defined(USE_CRYPTO_POLARSSL) + // XXX: PolarSSL 1.3.11: cipher_free_ctx deprecated, Use cipher_free() instead. + if (cipher_setkey(evp, true_key, enc_key_len * 8, enc) != 0) { + cipher_free_ctx(evp); + FATAL("Cannot set PolarSSL cipher key"); + } +#if POLARSSL_VERSION_NUMBER >= 0x01030000 + if (cipher_set_iv(evp, iv, iv_len) != 0) { + cipher_free_ctx(evp); + FATAL("Cannot set PolarSSL cipher IV"); + } + if (cipher_reset(evp) != 0) { + cipher_free_ctx(evp); + FATAL("Cannot finalize PolarSSL cipher context"); + } +#else + if (cipher_reset(evp, iv) != 0) { + cipher_free_ctx(evp); + FATAL("Cannot set PolarSSL cipher IV"); + } +#endif +#elif defined(USE_CRYPTO_MBEDTLS) + if (mbedtls_cipher_setkey(evp, true_key, enc_key_len * 8, enc) != 0) { + mbedtls_cipher_free(evp); + FATAL("Cannot set mbed TLS cipher key"); + } + + if (mbedtls_cipher_set_iv(evp, iv, iv_len) != 0) { + mbedtls_cipher_free(evp); + FATAL("Cannot set mbed TLS cipher IV"); + } + if (mbedtls_cipher_reset(evp) != 0) { + mbedtls_cipher_free(evp); + FATAL("Cannot finalize mbed TLS cipher context"); + } +#endif + +#ifdef DEBUG + dump("IV", (char *)iv, iv_len); +#endif +} + +void +cipher_context_release(cipher_ctx_t *ctx) +{ + if (enc_method >= SALSA20) { + return; + } + +#ifdef USE_CRYPTO_APPLECC + cipher_cc_t *cc = &ctx->cc; + if (cc->cryptor != NULL) { + CCCryptorRelease(cc->cryptor); + cc->cryptor = NULL; + } + if (cc->valid == kCCContextValid) { + return; + } +#endif + +#if defined(USE_CRYPTO_OPENSSL) + EVP_CIPHER_CTX_free(ctx->evp); +#elif defined(USE_CRYPTO_POLARSSL) +// NOTE: cipher_free_ctx deprecated in PolarSSL 1.3.11 + cipher_free_ctx(ctx->evp); + ss_free(ctx->evp); +#elif defined(USE_CRYPTO_MBEDTLS) +// NOTE: cipher_free_ctx deprecated + mbedtls_cipher_free(ctx->evp); + ss_free(ctx->evp); +#endif +} + +static int +cipher_context_update(cipher_ctx_t *ctx, uint8_t *output, size_t *olen, + const uint8_t *input, size_t ilen) +{ +#ifdef USE_CRYPTO_APPLECC + cipher_cc_t *cc = &ctx->cc; + if (cc->valid == kCCContextValid) { + CCCryptorStatus ret; + ret = CCCryptorUpdate(cc->cryptor, input, ilen, output, + ilen, olen); + return (ret == kCCSuccess) ? 1 : 0; + } +#endif + cipher_evp_t *evp = ctx->evp; +#if defined(USE_CRYPTO_OPENSSL) + int err = 0, tlen = *olen; + err = EVP_CipherUpdate(evp, (uint8_t *)output, &tlen, + (const uint8_t *)input, ilen); + *olen = tlen; + return err; +#elif defined(USE_CRYPTO_POLARSSL) + return !cipher_update(evp, (const uint8_t *)input, ilen, + (uint8_t *)output, olen); +#elif defined(USE_CRYPTO_MBEDTLS) + return !mbedtls_cipher_update(evp, (const uint8_t *)input, ilen, + (uint8_t *)output, olen); +#endif +} +int ss_md5_hmac(char *auth, char *msg, int msg_len, uint8_t *iv) +{ + uint8_t hash[MD5_BYTES]; + uint8_t auth_key[MAX_IV_LENGTH + MAX_KEY_LENGTH]; + memcpy(auth_key, iv, enc_iv_len); + memcpy(auth_key + enc_iv_len, enc_key, enc_key_len); + +#if defined(USE_CRYPTO_OPENSSL) + HMAC(EVP_md5(), auth_key, enc_iv_len + enc_key_len, (uint8_t *)msg, msg_len, (uint8_t *)hash, NULL); +#elif defined(USE_CRYPTO_MBEDTLS) + mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_MD5), auth_key, enc_iv_len + enc_key_len, (uint8_t *)msg, msg_len, (uint8_t *)hash); +#else + md5_hmac(auth_key, enc_iv_len + enc_key_len, (uint8_t *)msg, msg_len, (uint8_t *)hash); +#endif + + memcpy(auth, hash, MD5_BYTES); + + return 0; +} + +int ss_md5_hmac_with_key(char *auth, char *msg, int msg_len, uint8_t *auth_key, int key_len) +{ + uint8_t hash[MD5_BYTES]; + +#if defined(USE_CRYPTO_OPENSSL) + HMAC(EVP_md5(), auth_key, key_len, (uint8_t *)msg, msg_len, (uint8_t *)hash, NULL); +#elif defined(USE_CRYPTO_MBEDTLS) + mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_MD5), auth_key, key_len, (uint8_t *)msg, msg_len, (uint8_t *)hash); +#else + md5_hmac(auth_key, key_len, (uint8_t *)msg, msg_len, (uint8_t *)hash); +#endif + + memcpy(auth, hash, MD5_BYTES); + + return 0; +} + +int ss_md5_hash_func(char *auth, char *msg, int msg_len) +{ + uint8_t hash[MD5_BYTES]; + +#if defined(USE_CRYPTO_OPENSSL) + MD5((uint8_t *)msg, msg_len, (uint8_t *)hash); +#elif defined(USE_CRYPTO_MBEDTLS) + mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_MD5), (uint8_t *)msg, msg_len, (uint8_t *)hash); +#else + md5((uint8_t *)msg, msg_len, (uint8_t *)hash); +#endif + + memcpy(auth, hash, MD5_BYTES); + + return 0; +} + +int ss_sha1_hmac(char *auth, char *msg, int msg_len, uint8_t *iv) +{ + uint8_t hash[SHA1_BYTES]; + uint8_t auth_key[MAX_IV_LENGTH + MAX_KEY_LENGTH]; + memcpy(auth_key, iv, enc_iv_len); + memcpy(auth_key + enc_iv_len, enc_key, enc_key_len); + +#if defined(USE_CRYPTO_OPENSSL) + HMAC(EVP_sha1(), auth_key, enc_iv_len + enc_key_len, (uint8_t *)msg, msg_len, (uint8_t *)hash, NULL); +#elif defined(USE_CRYPTO_MBEDTLS) + mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA1), auth_key, enc_iv_len + enc_key_len, (uint8_t *)msg, msg_len, (uint8_t *)hash); +#else + sha1_hmac(auth_key, enc_iv_len + enc_key_len, (uint8_t *)msg, msg_len, (uint8_t *)hash); +#endif + + memcpy(auth, hash, SHA1_BYTES); + + return 0; +} + +int ss_sha1_hmac_with_key(char *auth, char *msg, int msg_len, uint8_t *auth_key, int key_len) +{ + uint8_t hash[SHA1_BYTES]; + +#if defined(USE_CRYPTO_OPENSSL) + HMAC(EVP_sha1(), auth_key, key_len, (uint8_t *)msg, msg_len, (uint8_t *)hash, NULL); +#elif defined(USE_CRYPTO_MBEDTLS) + mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA1), auth_key, key_len, (uint8_t *)msg, msg_len, (uint8_t *)hash); +#else + sha1_hmac(auth_key, key_len, (uint8_t *)msg, msg_len, (uint8_t *)hash); +#endif + + memcpy(auth, hash, SHA1_BYTES); + + return 0; +} + +int ss_sha1_hash_func(char *auth, char *msg, int msg_len) +{ + uint8_t hash[SHA1_BYTES]; +#if defined(USE_CRYPTO_OPENSSL) + SHA1((uint8_t *)msg, msg_len, (uint8_t *)hash); +#elif defined(USE_CRYPTO_MBEDTLS) + mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA1), (uint8_t *)msg, msg_len, (uint8_t *)hash); +#else + sha1((uint8_t *)msg, msg_len, (uint8_t *)hash); +#endif + + memcpy(auth, hash, SHA1_BYTES); + + return 0; +} + +int ss_aes_128_cbc(char *encrypt, char *out_data, char *key) +{ + unsigned char iv[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + +#if defined(USE_CRYPTO_OPENSSL) + AES_KEY aes; + AES_set_encrypt_key((unsigned char*)key, 128, &aes); + AES_cbc_encrypt((const unsigned char *)encrypt, (unsigned char *)out_data, 16, &aes, iv, AES_ENCRYPT); + +#elif defined(USE_CRYPTO_MBEDTLS) + mbedtls_aes_context aes; + + unsigned char output[16]; + + mbedtls_aes_setkey_enc( &aes, (unsigned char *)key, 128 ); + mbedtls_aes_crypt_cbc( &aes, MBEDTLS_AES_ENCRYPT, 16, iv, (unsigned char *)encrypt, output ); + + memcpy(out_data, output, 16); +#else + + aes_context aes; + + unsigned char output[16]; + + aes_setkey_enc( &aes, (unsigned char *)key, 128 ); + aes_crypt_cbc( &aes, AES_ENCRYPT, 16, iv, (unsigned char *)encrypt, output ); + + memcpy(out_data, output, 16); +#endif + + return 0; +} + +int ss_onetimeauth(buffer_t *buf, uint8_t *iv, size_t capacity) +{ + uint8_t hash[ONETIMEAUTH_BYTES * 2]; + uint8_t auth_key[MAX_IV_LENGTH + MAX_KEY_LENGTH]; + memcpy(auth_key, iv, enc_iv_len); + memcpy(auth_key + enc_iv_len, enc_key, enc_key_len); + + brealloc(buf, ONETIMEAUTH_BYTES + buf->len, capacity); + +#if defined(USE_CRYPTO_OPENSSL) + HMAC(EVP_sha1(), auth_key, enc_iv_len + enc_key_len, (uint8_t *)buf->array, buf->len, (uint8_t *)hash, NULL); +#elif defined(USE_CRYPTO_MBEDTLS) + mbedtls_md_hmac(mbedtls_md_info_from_type( + MBEDTLS_MD_SHA1), auth_key, enc_iv_len + enc_key_len, (uint8_t *)buf->array, buf->len, + (uint8_t *)hash); +#else + sha1_hmac(auth_key, enc_iv_len + enc_key_len, (uint8_t *)buf->array, buf->len, (uint8_t *)hash); +#endif + + memcpy(buf->array + buf->len, hash, ONETIMEAUTH_BYTES); + buf->len += ONETIMEAUTH_BYTES; + + return 0; +} + +int +ss_onetimeauth_verify(buffer_t *buf, uint8_t *iv) +{ + uint8_t hash[ONETIMEAUTH_BYTES * 2]; + uint8_t auth_key[MAX_IV_LENGTH + MAX_KEY_LENGTH]; + memcpy(auth_key, iv, enc_iv_len); + memcpy(auth_key + enc_iv_len, enc_key, enc_key_len); + size_t len = buf->len - ONETIMEAUTH_BYTES; + +#if defined(USE_CRYPTO_OPENSSL) + HMAC(EVP_sha1(), auth_key, enc_iv_len + enc_key_len, (uint8_t *)buf->array, len, hash, NULL); +#elif defined(USE_CRYPTO_MBEDTLS) + mbedtls_md_hmac(mbedtls_md_info_from_type( + MBEDTLS_MD_SHA1), auth_key, enc_iv_len + enc_key_len, (uint8_t *)buf->array, len, hash); +#else + sha1_hmac(auth_key, enc_iv_len + enc_key_len, (uint8_t *)buf->array, len, hash); +#endif + + return safe_memcmp(buf->array + len, hash, ONETIMEAUTH_BYTES); +} + +int +ss_encrypt_all(buffer_t *plain, int method, int auth, size_t capacity) +{ + if (method > TABLE) { + cipher_ctx_t evp; + cipher_context_init(&evp, method, 1); + + size_t iv_len = enc_iv_len; + int err = 1; + + static buffer_t tmp = { 0, 0, 0, NULL }; + brealloc(&tmp, iv_len + plain->len, capacity); + buffer_t *cipher = &tmp; + cipher->len = plain->len; + + uint8_t iv[MAX_IV_LENGTH]; + + rand_bytes(iv, iv_len); + cipher_context_set_iv(&evp, iv, iv_len, 1); + memcpy(cipher->array, iv, iv_len); + + if (auth) { + ss_onetimeauth(plain, iv, capacity); + cipher->len = plain->len; + } + + if (method >= SALSA20) { + crypto_stream_xor_ic((uint8_t *)(cipher->array + iv_len), + (const uint8_t *)plain->array, (uint64_t)(plain->len), + (const uint8_t *)iv, + 0, enc_key, method); + } else { + err = cipher_context_update(&evp, (uint8_t *)(cipher->array + iv_len), + &cipher->len, (const uint8_t *)plain->array, + plain->len); + } + + if (!err) { + bfree(plain); + cipher_context_release(&evp); + return -1; + } + +#ifdef DEBUG + dump("PLAIN", plain->array, plain->len); + dump("CIPHER", cipher->array + iv_len, cipher->len); +#endif + + cipher_context_release(&evp); + + brealloc(plain, iv_len + cipher->len, capacity); + memcpy(plain->array, cipher->array, iv_len + cipher->len); + plain->len = iv_len + cipher->len; + + return 0; + } else { + char *begin = plain->array; + char *ptr = plain->array; + while (ptr < begin + plain->len) { + *ptr = (char)enc_table[(uint8_t)*ptr]; + ptr++; + } + return 0; + } +} + +int +ss_encrypt(buffer_t *plain, enc_ctx_t *ctx, size_t capacity) +{ + if (ctx != NULL) { + static buffer_t tmp = { 0, 0, 0, NULL }; + + int err = 1; + size_t iv_len = 0; + if (!ctx->init) { + iv_len = enc_iv_len; + } + + brealloc(&tmp, iv_len + plain->len, capacity); + buffer_t *cipher = &tmp; + cipher->len = plain->len; + + if (!ctx->init) { + cipher_context_set_iv(&ctx->evp, ctx->evp.iv, iv_len, 1); + memcpy(cipher->array, ctx->evp.iv, iv_len); + ctx->counter = 0; + ctx->init = 1; + } + + if (enc_method >= SALSA20) { + int padding = ctx->counter % SODIUM_BLOCK_SIZE; + brealloc(cipher, iv_len + (padding + cipher->len) * 2, capacity); + if (padding) { + brealloc(plain, plain->len + padding, capacity); + memmove(plain->array + padding, plain->array, plain->len); + sodium_memzero(plain->array, padding); + } + crypto_stream_xor_ic((uint8_t *)(cipher->array + iv_len), + (const uint8_t *)plain->array, + (uint64_t)(plain->len + padding), + (const uint8_t *)ctx->evp.iv, + ctx->counter / SODIUM_BLOCK_SIZE, enc_key, + enc_method); + ctx->counter += plain->len; + if (padding) { + memmove(cipher->array + iv_len, + cipher->array + iv_len + padding, cipher->len); + } + } else { + err = + cipher_context_update(&ctx->evp, + (uint8_t *)(cipher->array + iv_len), + &cipher->len, (const uint8_t *)plain->array, + plain->len); + if (!err) { + return -1; + } + } + +#ifdef DEBUG + dump("PLAIN", plain->array, plain->len); + dump("CIPHER", cipher->array + iv_len, cipher->len); +#endif + + brealloc(plain, iv_len + cipher->len, capacity); + memcpy(plain->array, cipher->array, iv_len + cipher->len); + plain->len = iv_len + cipher->len; + + return 0; + } else { + char *begin = plain->array; + char *ptr = plain->array; + while (ptr < begin + plain->len) { + *ptr = (char)enc_table[(uint8_t)*ptr]; + ptr++; + } + return 0; + } +} + +int +ss_decrypt_all(buffer_t *cipher, int method, int auth, size_t capacity) +{ + if (method > TABLE) { + size_t iv_len = enc_iv_len; + int ret = 1; + + if (cipher->len <= iv_len) { + return -1; + } + + cipher_ctx_t evp; + cipher_context_init(&evp, method, 0); + + static buffer_t tmp = { 0, 0, 0, NULL }; + brealloc(&tmp, cipher->len, capacity); + buffer_t *plain = &tmp; + plain->len = cipher->len - iv_len; + + uint8_t iv[MAX_IV_LENGTH]; + memcpy(iv, cipher->array, iv_len); + cipher_context_set_iv(&evp, iv, iv_len, 0); + + if (method >= SALSA20) { + crypto_stream_xor_ic((uint8_t *)plain->array, + (const uint8_t *)(cipher->array + iv_len), + (uint64_t)(cipher->len - iv_len), + (const uint8_t *)iv, 0, enc_key, method); + } else { + ret = cipher_context_update(&evp, (uint8_t *)plain->array, &plain->len, + (const uint8_t *)(cipher->array + iv_len), + cipher->len - iv_len); + } + + if (auth || (plain->array[0] & ONETIMEAUTH_FLAG)) { + if (plain->len > ONETIMEAUTH_BYTES) { + ret = !ss_onetimeauth_verify(plain, iv); + if (ret) { + plain->len -= ONETIMEAUTH_BYTES; + } + } else { + ret = 0; + } + } + + if (!ret) { + bfree(cipher); + cipher_context_release(&evp); + return -1; + } + +#ifdef DEBUG + dump("PLAIN", plain->array, plain->len); + dump("CIPHER", cipher->array + iv_len, cipher->len - iv_len); +#endif + + cipher_context_release(&evp); + + brealloc(cipher, plain->len, capacity); + memcpy(cipher->array, plain->array, plain->len); + cipher->len = plain->len; + + return 0; + } else { + char *begin = cipher->array; + char *ptr = cipher->array; + while (ptr < begin + cipher->len) { + *ptr = (char)dec_table[(uint8_t)*ptr]; + ptr++; + } + return 0; + } +} + +int +ss_decrypt(buffer_t *cipher, enc_ctx_t *ctx, size_t capacity) +{ + if (ctx != NULL) { + static buffer_t tmp = { 0, 0, 0, NULL }; + + size_t iv_len = 0; + int err = 1; + + brealloc(&tmp, cipher->len, capacity); + buffer_t *plain = &tmp; + plain->len = cipher->len; + + if (!ctx->init) { + uint8_t iv[MAX_IV_LENGTH]; + iv_len = enc_iv_len; + plain->len -= iv_len; + + memcpy(iv, cipher->array, iv_len); + cipher_context_set_iv(&ctx->evp, iv, iv_len, 0); + ctx->counter = 0; + ctx->init = 1; + + if (enc_method > RC4) { + if (cache_key_exist(iv_cache, (char *)iv, iv_len)) { + bfree(cipher); + return -1; + } else { + cache_insert(iv_cache, (char *)iv, iv_len, NULL); + } + } + } + + if (enc_method >= SALSA20) { + int padding = ctx->counter % SODIUM_BLOCK_SIZE; + brealloc(plain, (plain->len + padding) * 2, capacity); + + if (padding) { + brealloc(cipher, cipher->len + padding, capacity); + memmove(cipher->array + iv_len + padding, cipher->array + iv_len, + cipher->len - iv_len); + sodium_memzero(cipher->array + iv_len, padding); + } + crypto_stream_xor_ic((uint8_t *)plain->array, + (const uint8_t *)(cipher->array + iv_len), + (uint64_t)(cipher->len - iv_len + padding), + (const uint8_t *)ctx->evp.iv, + ctx->counter / SODIUM_BLOCK_SIZE, enc_key, + enc_method); + ctx->counter += cipher->len - iv_len; + if (padding) { + memmove(plain->array, plain->array + padding, plain->len); + } + } else { + err = cipher_context_update(&ctx->evp, (uint8_t *)plain->array, &plain->len, + (const uint8_t *)(cipher->array + iv_len), + cipher->len - iv_len); + } + + if (!err) { + bfree(cipher); + return -1; + } + +#ifdef DEBUG + dump("PLAIN", plain->array, plain->len); + dump("CIPHER", cipher->array + iv_len, cipher->len - iv_len); +#endif + + brealloc(cipher, plain->len, capacity); + memcpy(cipher->array, plain->array, plain->len); + cipher->len = plain->len; + + return 0; + } else { + char *begin = cipher->array; + char *ptr = cipher->array; + while (ptr < begin + cipher->len) { + *ptr = (char)dec_table[(uint8_t)*ptr]; + ptr++; + } + return 0; + } +} + +void +enc_ctx_init(int method, enc_ctx_t *ctx, int enc) +{ + sodium_memzero(ctx, sizeof(enc_ctx_t)); + cipher_context_init(&ctx->evp, method, enc); + + if (enc) { + rand_bytes(ctx->evp.iv, enc_iv_len); + } +} + +void +enc_key_init(int method, const char *pass) +{ + if (method <= TABLE || method >= CIPHER_NUM) { + LOGE("enc_key_init(): Illegal method"); + return; + } + + // Initialize cache + cache_create(&iv_cache, 256, NULL); + +#if defined(USE_CRYPTO_OPENSSL) + OpenSSL_add_all_algorithms(); +#else + cipher_kt_t cipher_info; +#endif + + cipher_t cipher; + memset(&cipher, 0, sizeof(cipher_t)); + + // Initialize sodium for random generator + if (sodium_init() == -1) { + FATAL("Failed to initialize sodium"); + } + + if (method == SALSA20 || method == CHACHA20 || method == CHACHA20IETF) { +#if defined(USE_CRYPTO_OPENSSL) + cipher.info = NULL; + cipher.key_len = supported_ciphers_key_size[method]; + cipher.iv_len = supported_ciphers_iv_size[method]; +#endif +#if defined(USE_CRYPTO_POLARSSL) + cipher.info = &cipher_info; + cipher.info->base = NULL; + cipher.info->key_length = supported_ciphers_key_size[method] * 8; + cipher.info->iv_size = supported_ciphers_iv_size[method]; +#endif +#if defined(USE_CRYPTO_MBEDTLS) + // XXX: key_length changed to key_bitlen in mbed TLS 2.0.0 + cipher.info = &cipher_info; + cipher.info->base = NULL; + cipher.info->key_bitlen = supported_ciphers_key_size[method] * 8; + cipher.info->iv_size = supported_ciphers_iv_size[method]; +#endif + } else { + cipher.info = (cipher_kt_t *)get_cipher_type(method); + } + + if (cipher.info == NULL && cipher.key_len == 0) { + do { +#if defined(USE_CRYPTO_POLARSSL) && defined(USE_CRYPTO_APPLECC) + if (supported_ciphers_applecc[method] != kCCAlgorithmInvalid) { + cipher_info.base = NULL; + cipher_info.key_length = supported_ciphers_key_size[method] * 8; + cipher_info.iv_size = supported_ciphers_iv_size[method]; + cipher.info = (cipher_kt_t *)&cipher_info; + break; + } +#endif +#if defined(USE_CRYPTO_MBEDTLS) && defined(USE_CRYPTO_APPLECC) + // XXX: key_length changed to key_bitlen in mbed TLS 2.0.0 + if (supported_ciphers_applecc[method] != kCCAlgorithmInvalid) { + cipher_info.base = NULL; + cipher_info.key_bitlen = supported_ciphers_key_size[method] * 8; + cipher_info.iv_size = supported_ciphers_iv_size[method]; + cipher.info = (cipher_kt_t *)&cipher_info; + break; + } +#endif + LOGE("Cipher %s not found in crypto library", supported_ciphers[method]); + FATAL("Cannot initialize cipher"); + } while (0); + } + + const digest_type_t *md = get_digest_type("MD5"); + if (md == NULL) { + FATAL("MD5 Digest not found in crypto library"); + } + + enc_key_len = bytes_to_key(&cipher, md, (const uint8_t *)pass, enc_key); + + if (enc_key_len == 0) { + FATAL("Cannot generate key and IV"); + } + if (method == RC4_MD5 || method == RC4_MD5_6) { + enc_iv_len = supported_ciphers_iv_size[method]; + } else { + enc_iv_len = cipher_iv_size(&cipher); + } + enc_method = method; +} + +int +enc_init(const char *pass, const char *method) +{ + int m = TABLE; + if (method != NULL) { + for (m = TABLE; m < CIPHER_NUM; m++) + if (strcmp(method, supported_ciphers[m]) == 0) { + break; + } + if (m >= CIPHER_NUM) { + LOGE("Invalid cipher name: %s, use rc4-md5 instead", method); + m = RC4_MD5; + } + } + if (m == TABLE) { + enc_table_init(pass); + } else { + enc_key_init(m, pass); + } + return m; +} + +int +ss_check_hash(buffer_t *buf, chunk_t *chunk, enc_ctx_t *ctx, size_t capacity) +{ + int i, j, k; + ssize_t blen = buf->len; + uint32_t cidx = chunk->idx; + + brealloc(chunk->buf, chunk->len + blen, capacity); + brealloc(buf, chunk->len + blen, capacity); + + for (i = 0, j = 0, k = 0; i < blen; i++) { + chunk->buf->array[cidx++] = buf->array[k++]; + + if (cidx == CLEN_BYTES) { + uint16_t clen = ntohs(*((uint16_t *)chunk->buf->array)); + brealloc(chunk->buf, clen + AUTH_BYTES, capacity); + chunk->len = clen; + } + + if (cidx == chunk->len + AUTH_BYTES) { + // Compare hash + uint8_t hash[ONETIMEAUTH_BYTES * 2]; + uint8_t key[MAX_IV_LENGTH + sizeof(uint32_t)]; + + uint32_t c = htonl(chunk->counter); + memcpy(key, ctx->evp.iv, enc_iv_len); + memcpy(key + enc_iv_len, &c, sizeof(uint32_t)); +#if defined(USE_CRYPTO_OPENSSL) + HMAC(EVP_sha1(), key, enc_iv_len + sizeof(uint32_t), + (uint8_t *)chunk->buf->array + AUTH_BYTES, chunk->len, hash, NULL); +#elif defined(USE_CRYPTO_MBEDTLS) + mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA1), key, enc_iv_len + sizeof(uint32_t), + (uint8_t *)chunk->buf->array + AUTH_BYTES, chunk->len, hash); +#else + sha1_hmac(key, enc_iv_len + sizeof(uint32_t), + (uint8_t *)chunk->buf->array + AUTH_BYTES, chunk->len, hash); +#endif + + if (safe_memcmp(hash, chunk->buf->array + CLEN_BYTES, ONETIMEAUTH_BYTES) != 0) { + return 0; + } + + // Copy chunk back to buffer + memmove(buf->array + j + chunk->len, buf->array + k, blen - i - 1); + memcpy(buf->array + j, chunk->buf->array + AUTH_BYTES, chunk->len); + + // Reset the base offset + j += chunk->len; + k = j; + cidx = 0; + chunk->counter++; + } + } + + buf->len = j; + chunk->idx = cidx; + return 1; +} + +int +ss_gen_hash(buffer_t *buf, uint32_t *counter, enc_ctx_t *ctx, size_t capacity) +{ + ssize_t blen = buf->len; + uint16_t chunk_len = htons((uint16_t)blen); + uint8_t hash[ONETIMEAUTH_BYTES * 2]; + uint8_t key[MAX_IV_LENGTH + sizeof(uint32_t)]; + uint32_t c = htonl(*counter); + + brealloc(buf, AUTH_BYTES + blen, capacity); + memcpy(key, ctx->evp.iv, enc_iv_len); + memcpy(key + enc_iv_len, &c, sizeof(uint32_t)); +#if defined(USE_CRYPTO_OPENSSL) + HMAC(EVP_sha1(), key, enc_iv_len + sizeof(uint32_t), (uint8_t *)buf->array, blen, hash, NULL); +#elif defined(USE_CRYPTO_MBEDTLS) + mbedtls_md_hmac(mbedtls_md_info_from_type( + MBEDTLS_MD_SHA1), key, enc_iv_len + sizeof(uint32_t), (uint8_t *)buf->array, blen, hash); +#else + sha1_hmac(key, enc_iv_len + sizeof(uint32_t), (uint8_t *)buf->array, blen, hash); +#endif + + memmove(buf->array + AUTH_BYTES, buf->array, blen); + memcpy(buf->array + CLEN_BYTES, hash, ONETIMEAUTH_BYTES); + memcpy(buf->array, &chunk_len, CLEN_BYTES); + + *counter = *counter + 1; + buf->len = blen + AUTH_BYTES; + + return 0; +} diff --git a/shadowsocksr-libev/src/server/encrypt.h b/shadowsocksr-libev/src/server/encrypt.h new file mode 100644 index 000000000..3bb7940aa --- /dev/null +++ b/shadowsocksr-libev/src/server/encrypt.h @@ -0,0 +1,222 @@ +/* + * encrypt.h - Define the enryptor's interface + * + * Copyright (C) 2013 - 2016, Max Lv + * + * This file is part of the shadowsocks-libev. + * + * shadowsocks-libev is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * shadowsocks-libev 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with shadowsocks-libev; see the file COPYING. If not, see + * . + */ + +#ifndef _ENCRYPT_H +#define _ENCRYPT_H + +#ifndef __MINGW32__ +#include +#else + +#ifdef max +#undef max +#endif + +#ifdef min +#undef min +#endif + +#endif + +#include +#include +#include +#include + +#if defined(USE_CRYPTO_OPENSSL) + +#include +#include +#include +typedef EVP_CIPHER cipher_kt_t; +typedef EVP_CIPHER_CTX cipher_evp_t; +typedef EVP_MD digest_type_t; +#define MAX_KEY_LENGTH EVP_MAX_KEY_LENGTH +#define MAX_IV_LENGTH EVP_MAX_IV_LENGTH +#define MAX_MD_SIZE EVP_MAX_MD_SIZE + +#elif defined(USE_CRYPTO_POLARSSL) + +#include +#include +typedef cipher_info_t cipher_kt_t; +typedef cipher_context_t cipher_evp_t; +typedef md_info_t digest_type_t; +#define MAX_KEY_LENGTH 64 +#define MAX_IV_LENGTH POLARSSL_MAX_IV_LENGTH +#define MAX_MD_SIZE POLARSSL_MD_MAX_SIZE + +#elif defined(USE_CRYPTO_MBEDTLS) + +#include +#include +typedef mbedtls_cipher_info_t cipher_kt_t; +typedef mbedtls_cipher_context_t cipher_evp_t; +typedef mbedtls_md_info_t digest_type_t; +#define MAX_KEY_LENGTH 64 +#define MAX_IV_LENGTH MBEDTLS_MAX_IV_LENGTH +#define MAX_MD_SIZE MBEDTLS_MD_MAX_SIZE + +/* we must have MBEDTLS_CIPHER_MODE_CFB defined */ +#if !defined(MBEDTLS_CIPHER_MODE_CFB) +#error Cipher Feedback mode a.k.a CFB not supported by your mbed TLS. +#endif + +#endif + +#ifdef USE_CRYPTO_APPLECC + +#include + +#define kCCAlgorithmInvalid UINT32_MAX +#define kCCContextValid 0 +#define kCCContextInvalid -1 + +typedef struct { + CCCryptorRef cryptor; + int valid; + CCOperation encrypt; + CCAlgorithm cipher; + CCMode mode; + CCPadding padding; + uint8_t iv[MAX_IV_LENGTH]; + uint8_t key[MAX_KEY_LENGTH]; + size_t iv_len; + size_t key_len; +} cipher_cc_t; + +#endif + +typedef struct { + cipher_evp_t *evp; +#ifdef USE_CRYPTO_APPLECC + cipher_cc_t cc; +#endif + uint8_t iv[MAX_IV_LENGTH]; +} cipher_ctx_t; + +typedef struct { + cipher_kt_t *info; + size_t iv_len; + size_t key_len; +} cipher_t; + +#ifdef HAVE_STDINT_H +#include +#elif HAVE_INTTYPES_H +#include +#endif + +#define SODIUM_BLOCK_SIZE 64 + +enum crpher_index { + NONE = -1, + TABLE = 0, + RC4, + RC4_MD5_6, + RC4_MD5, + AES_128_CFB, + AES_192_CFB, + AES_256_CFB, + AES_128_CTR, + AES_192_CTR, + AES_256_CTR, + BF_CFB, + CAMELLIA_128_CFB, + CAMELLIA_192_CFB, + CAMELLIA_256_CFB, + CAST5_CFB, + DES_CFB, + IDEA_CFB, + RC2_CFB, + SEED_CFB, + SALSA20, + CHACHA20, + CHACHA20IETF, + CIPHER_NUM, +}; + +#define ONETIMEAUTH_FLAG 0x10 +#define ADDRTYPE_MASK 0xEF + +#define ONETIMEAUTH_BYTES 10U +#define MD5_BYTES 16U +#define SHA1_BYTES 20U +#define CLEN_BYTES 2U +#define AUTH_BYTES (ONETIMEAUTH_BYTES + CLEN_BYTES) + +#define min(a, b) (((a) < (b)) ? (a) : (b)) +#define max(a, b) (((a) > (b)) ? (a) : (b)) + +typedef struct buffer { + size_t idx; + size_t len; + size_t capacity; + char *array; +} buffer_t; + +typedef struct chunk { + uint32_t idx; + uint32_t len; + uint32_t counter; + buffer_t *buf; +} chunk_t; + +typedef struct enc_ctx { + uint8_t init; + uint64_t counter; + cipher_ctx_t evp; +} enc_ctx_t; + +void bytes_to_key_with_size(const char *pass, size_t len, uint8_t *md, size_t md_size); + +int ss_encrypt_all(buffer_t *plaintext, int method, int auth, size_t capacity); +int ss_decrypt_all(buffer_t *ciphertext, int method, int auth, size_t capacity); +int ss_encrypt(buffer_t *plaintext, enc_ctx_t *ctx, size_t capacity); +int ss_decrypt(buffer_t *ciphertext, enc_ctx_t *ctx, size_t capacity); + +void enc_ctx_init(int method, enc_ctx_t *ctx, int enc); +int enc_init(const char *pass, const char *method); +int enc_get_iv_len(void); +uint8_t* enc_get_key(void); +int enc_get_key_len(void); +void cipher_context_release(cipher_ctx_t *evp); +unsigned char *enc_md5(const unsigned char *d, size_t n, unsigned char *md); + +int ss_md5_hmac(char *auth, char *msg, int msg_len, uint8_t *iv); +int ss_md5_hmac_with_key(char *auth, char *msg, int msg_len, uint8_t *auth_key, int key_len); +int ss_md5_hash_func(char *auth, char *msg, int msg_len); +int ss_sha1_hmac(char *auth, char *msg, int msg_len, uint8_t *iv); +int ss_sha1_hmac_with_key(char *auth, char *msg, int msg_len, uint8_t *auth_key, int key_len); +int ss_sha1_hash_func(char *auth, char *msg, int msg_len); +int ss_aes_128_cbc(char *encrypt, char *out_data, char *key); +int ss_onetimeauth(buffer_t *buf, uint8_t *iv, size_t capacity); +int ss_onetimeauth_verify(buffer_t *buf, uint8_t *iv); + +int ss_check_hash(buffer_t *buf, chunk_t *chunk, enc_ctx_t *ctx, size_t capacity); +int ss_gen_hash(buffer_t *buf, uint32_t *counter, enc_ctx_t *ctx, size_t capacity); + +int balloc(buffer_t *ptr, size_t capacity); +int brealloc(buffer_t *ptr, size_t len, size_t capacity); +void bfree(buffer_t *ptr); + +#endif // _ENCRYPT_H diff --git a/shadowsocksr-libev/src/server/http.c b/shadowsocksr-libev/src/server/http.c new file mode 100644 index 000000000..3bd4a3227 --- /dev/null +++ b/shadowsocksr-libev/src/server/http.c @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2011 and 2012, Dustin Lundquist + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include /* malloc() */ +#include /* strncpy() */ +#include /* strncasecmp() */ +#include /* isblank() */ + +#include "http.h" +#include "protocol.h" + +#define SERVER_NAME_LEN 256 + +static int parse_http_header(const char *, size_t, char **); +static int get_header(const char *, const char *, int, char **); +static int next_header(const char **, int *); + +static const protocol_t http_protocol_st = { + .default_port = 80, + .parse_packet = &parse_http_header, +}; +const protocol_t *const http_protocol = &http_protocol_st; + +/* + * Parses a HTTP request for the Host: header + * + * Returns: + * >=0 - length of the hostname and updates *hostname + * caller is responsible for freeing *hostname + * -1 - Incomplete request + * -2 - No Host header included in this request + * -3 - Invalid hostname pointer + * -4 - malloc failure + * < -4 - Invalid HTTP request + * + */ +static int +parse_http_header(const char *data, size_t data_len, char **hostname) +{ + int result, i; + + if (hostname == NULL) + return -3; + + if (data_len == 0) + return -1; + + result = get_header("Host:", data, data_len, hostname); + if (result < 0) + return result; + + /* + * if the user specifies the port in the request, it is included here. + * Host: example.com:80 + * so we trim off port portion + */ + for (i = result - 1; i >= 0; i--) + if ((*hostname)[i] == ':') { + (*hostname)[i] = '\0'; + result = i; + break; + } + + return result; +} + +static int +get_header(const char *header, const char *data, int data_len, char **value) +{ + int len, header_len; + + header_len = strlen(header); + + /* loop through headers stopping at first blank line */ + while ((len = next_header(&data, &data_len)) != 0) + if (len > header_len && strncasecmp(header, data, header_len) == 0) { + /* Eat leading whitespace */ + while (header_len < len && isblank(data[header_len])) + header_len++; + + *value = malloc(len - header_len + 1); + if (*value == NULL) + return -4; + + strncpy(*value, data + header_len, len - header_len); + (*value)[len - header_len] = '\0'; + + return len - header_len; + } + + /* If there is no data left after reading all the headers then we do not + * have a complete HTTP request, there must be a blank line */ + if (data_len == 0) + return -1; + + return -2; +} + +static int +next_header(const char **data, int *len) +{ + int header_len; + + /* perhaps we can optimize this to reuse the value of header_len, rather + * than scanning twice. + * Walk our data stream until the end of the header */ + while (*len > 2 && (*data)[0] != '\r' && (*data)[1] != '\n') { + (*len)--; + (*data)++; + } + + /* advanced past the pair */ + *data += 2; + *len -= 2; + + /* Find the length of the next header */ + header_len = 0; + while (*len > header_len + 1 + && (*data)[header_len] != '\r' + && (*data)[header_len + 1] != '\n') + header_len++; + + return header_len; +} diff --git a/shadowsocksr-libev/src/server/http.h b/shadowsocksr-libev/src/server/http.h new file mode 100644 index 000000000..914815ae9 --- /dev/null +++ b/shadowsocksr-libev/src/server/http.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2011 and 2012, Dustin Lundquist + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef HTTP_H +#define HTTP_H + +#include +#include "protocol.h" + +const protocol_t *const http_protocol; + +#endif diff --git a/shadowsocksr-libev/src/server/http_simple.c b/shadowsocksr-libev/src/server/http_simple.c new file mode 100644 index 000000000..cee15c52e --- /dev/null +++ b/shadowsocksr-libev/src/server/http_simple.c @@ -0,0 +1,626 @@ + +#include "http_simple.h" + +static char* g_useragent[] = { + "Mozilla/5.0 (Windows NT 6.3; WOW64; rv:40.0) Gecko/20100101 Firefox/40.0", + "Mozilla/5.0 (Windows NT 6.3; WOW64; rv:40.0) Gecko/20100101 Firefox/44.0", + "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36", + "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", + "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:35.0) Gecko/20100101 Firefox/35.0", + "Mozilla/5.0 (compatible; WOW64; MSIE 10.0; Windows NT 6.2)", + "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", + "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.3; Trident/7.0; .NET4.0E; .NET4.0C)", + "Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko", + "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", + "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", + "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", +}; + +static int g_useragent_index = -1; + +typedef struct http_simple_local_data { + int has_sent_header; + int has_recv_header; + char *encode_buffer; + int host_matched; + char *recv_buffer; + int recv_buffer_size; +}http_simple_local_data; + +void http_simple_local_data_init(http_simple_local_data* local) { + local->has_sent_header = 0; + local->has_recv_header = 0; + local->encode_buffer = NULL; + + local->recv_buffer = malloc(0); + local->recv_buffer_size = 0; + + local->host_matched = 0; + + if (g_useragent_index == -1) { + g_useragent_index = xorshift128plus() % (sizeof(g_useragent) / sizeof(*g_useragent)); + } +} + +obfs * http_simple_new_obfs() { + obfs * self = new_obfs(); + self->l_data = malloc(sizeof(http_simple_local_data)); + http_simple_local_data_init((http_simple_local_data*)self->l_data); + return self; +} + +void http_simple_dispose(obfs *self) { + http_simple_local_data *local = (http_simple_local_data*)self->l_data; + if (local->encode_buffer != NULL) { + free(local->encode_buffer); + local->encode_buffer = NULL; + } + free(local); + dispose_obfs(self); +} + +char http_simple_hex(char c) { + if (c < 10) return c + '0'; + return c - 10 + 'a'; +} + +int get_data_from_http_header(char *data, char **outdata) { + char *delim = "\r\n"; + char *delim_hex = "%"; + int outlength = 0; + + char *buf = *outdata; + char *p_line; + p_line = strtok(data, delim); + + //while(p_line) + { + char *p_hex; + + p_hex = strtok(p_line, delim_hex); + + while((p_hex = strtok(NULL, delim_hex))) + { + char hex = 0; + + if(strlen(p_hex) <= 0) + { + continue; + } + + if(strlen(p_hex) > 2) + { + char *c_hex = (char*)malloc(2); + memcpy(c_hex, p_hex, 2); + hex = (char)strtol(c_hex, NULL, 16); + free(c_hex); + } + else + { + hex = (char)strtol(p_hex, NULL, 16); + } + + outlength += 1; + buf = (char*)realloc(buf, outlength); + buf[outlength - 1] = hex; + } + + //p_line = strtok(p_line, delim); + } + *outdata = buf; + return outlength; +} + +void get_host_from_http_header(char *data, char **host) { + char* data_begin = strstr(data, "Host: "); + + if(data_begin == NULL) + { + return; + } + + data_begin += 6; + char* data_end = strstr(data_begin, "\r\n"); + char* data_end_port = strstr(data_begin, ":"); + + int host_length = 0; + + if(data_end_port != NULL) + { + host_length = data_end_port - data_begin; + } + else + { + host_length = data_end - data_begin; + } + + if(host_length <= 0) + { + return; + } + + memset(*host, 0x00, 1024); + memcpy(*host, data_begin, host_length); +} + +void http_simple_encode_head(http_simple_local_data *local, char *data, int datalength) { + if (local->encode_buffer == NULL) { + local->encode_buffer = (char*)malloc(datalength * 3 + 1); + } + int pos = 0; + for (; pos < datalength; ++pos) { + local->encode_buffer[pos * 3] = '%'; + local->encode_buffer[pos * 3 + 1] = http_simple_hex(((unsigned char)data[pos] >> 4)); + local->encode_buffer[pos * 3 + 2] = http_simple_hex(data[pos] & 0xF); + } + local->encode_buffer[pos * 3] = 0; +} + +int http_simple_client_encode(obfs *self, char **pencryptdata, int datalength, size_t* capacity) { + char *encryptdata = *pencryptdata; + http_simple_local_data *local = (http_simple_local_data*)self->l_data; + if (local->has_sent_header) { + return datalength; + } + char hosts[1024]; + char * phost[128]; + int host_num = 0; + int pos; + char hostport[128]; + int head_size = self->server.head_len + (xorshift128plus() & 0x3F); + int outlength; + char * out_buffer = (char*)malloc(datalength + 2048); + char * body_buffer = NULL; + if (head_size > datalength) + head_size = datalength; + http_simple_encode_head(local, encryptdata, head_size); + if (self->server.param && strlen(self->server.param) == 0) + self->server.param = NULL; + strncpy(hosts, self->server.param ? self->server.param : self->server.host, sizeof hosts); + phost[host_num++] = hosts; + for (pos = 0; hosts[pos]; ++pos) { + if (hosts[pos] == ',') { + phost[host_num++] = &hosts[pos + 1]; + hosts[pos] = 0; + } else if (hosts[pos] == '#') { + char * body_pointer = &hosts[pos + 1]; + char * p; + int trans_char = 0; + p = body_buffer = (char*)malloc(2048); + for ( ; *body_pointer; ++body_pointer) { + if (*body_pointer == '\\') { + trans_char = 1; + continue; + } else if (*body_pointer == '\n') { + *p = '\r'; + *++p = '\n'; + continue; + } + if (trans_char) { + if (*body_pointer == '\\' ) { + *p = '\\'; + } else if (*body_pointer == 'n' ) { + *p = '\r'; + *++p = '\n'; + } else { + *p = '\\'; + *p = *body_pointer; + } + trans_char = 0; + } else { + *p = *body_pointer; + } + ++p; + } + *p = 0; + hosts[pos] = 0; + break; + } + } + host_num = xorshift128plus() % host_num; + if (self->server.port == 80) + sprintf(hostport, "%s", phost[host_num]); + else + sprintf(hostport, "%s:%d", phost[host_num], self->server.port); + if (body_buffer) { + sprintf(out_buffer, + "GET /%s HTTP/1.1\r\n" + "Host: %s\r\n" + "%s\r\n\r\n", + local->encode_buffer, + hostport, + body_buffer); + } else { + sprintf(out_buffer, + "GET /%s HTTP/1.1\r\n" + "Host: %s\r\n" + "User-Agent: %s\r\n" + "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n" + "Accept-Language: en-US,en;q=0.8\r\n" + "Accept-Encoding: gzip, deflate\r\n" + "DNT: 1\r\n" + "Connection: keep-alive\r\n" + "\r\n", + local->encode_buffer, + hostport, + g_useragent[g_useragent_index] + ); + } + //LOGI("http header: %s", out_buffer); + outlength = strlen(out_buffer); + memmove(out_buffer + outlength, encryptdata + head_size, datalength - head_size); + outlength += datalength - head_size; + local->has_sent_header = 1; + if (*capacity < outlength) { + *pencryptdata = (char*)realloc(*pencryptdata, *capacity = outlength * 2); + encryptdata = *pencryptdata; + } + memmove(encryptdata, out_buffer, outlength); + free(out_buffer); + if (body_buffer != NULL) + free(body_buffer); + if (local->encode_buffer != NULL) { + free(local->encode_buffer); + local->encode_buffer = NULL; + } + return outlength; +} + +int http_simple_server_encode(obfs *self, char **pencryptdata, int datalength, size_t* capacity) { + char *encryptdata = *pencryptdata; + http_simple_local_data *local = (http_simple_local_data*)self->l_data; + if (local->has_sent_header) { + return datalength; + } + int outlength; + char * out_buffer = (char*)malloc(datalength + 2048); + + time_t now; + struct tm *tm_now; + char datetime[200]; + + time(&now); + tm_now = localtime(&now); + strftime(datetime, 200, "%a, %d %b %Y %H:%M:%S GMT", tm_now); + + sprintf(out_buffer, + "HTTP/1.1 200 OK\r\nConnection: keep-alive\r\nContent-Encoding: gzip\r\nContent-Type: text/html\r\nDate: " + "%s" + "\r\nServer: nginx\r\nVary: Accept-Encoding\r\n\r\n", + datetime); + + outlength = strlen(out_buffer); + memmove(out_buffer + outlength, encryptdata, datalength); + outlength += datalength; + + local->has_sent_header = 1; + if (*capacity < outlength) { + *pencryptdata = (char*)realloc(*pencryptdata, *capacity = outlength * 2); + encryptdata = *pencryptdata; + } + memmove(encryptdata, out_buffer, outlength); + free(out_buffer); + return outlength; +} + +int http_simple_client_decode(obfs *self, char **pencryptdata, int datalength, size_t* capacity, int *needsendback) { + char *encryptdata = *pencryptdata; + http_simple_local_data *local = (http_simple_local_data*)self->l_data; + *needsendback = 0; + if (local->has_recv_header) { + return datalength; + } + char* data_begin = strstr(encryptdata, "\r\n\r\n"); + if (data_begin) { + int outlength; + data_begin += 4; + local->has_recv_header = 1; + outlength = datalength - (data_begin - encryptdata); + memmove(encryptdata, data_begin, outlength); + return outlength; + } else { + return 0; + } +} + +int http_simple_server_decode(obfs *self, char **pencryptdata, int datalength, size_t* capacity, int *needsendback) { + char *encryptdata = *pencryptdata; + http_simple_local_data *local = (http_simple_local_data*)self->l_data; + *needsendback = 0; + if (local->has_recv_header) { + return datalength; + } + + if(datalength != 0) + { + local->recv_buffer = (char*)realloc(local->recv_buffer, local->recv_buffer_size + datalength); + memmove(local->recv_buffer + local->recv_buffer_size, encryptdata, datalength); + local->recv_buffer_size += datalength; + + int outlength = local->recv_buffer_size; + if (*capacity < outlength) { + *pencryptdata = (char*)realloc(*pencryptdata, *capacity = outlength * 2); + encryptdata = *pencryptdata; + } + memcpy(encryptdata, local->recv_buffer, local->recv_buffer_size); + } + + if(local->recv_buffer_size > 10) + { + if(strstr(local->recv_buffer, "GET /") == local->recv_buffer || strstr(local->recv_buffer, "POST /") == local->recv_buffer) + { + if(local->recv_buffer_size > 65536) + { + free(local->recv_buffer); + local->recv_buffer = malloc(0); + local->recv_buffer_size = 0; + local->has_sent_header = 1; + local->has_recv_header = 1; + LOGE("http_simple: over size"); + return -1; + } + } + else + { + free(local->recv_buffer); + local->recv_buffer = malloc(0); + local->recv_buffer_size = 0; + local->has_sent_header = 1; + local->has_recv_header = 1; + LOGE("http_simple: not match begin"); + return -1; + } + } + else + { + LOGE("http_simple: too short"); + local->has_sent_header = 1; + local->has_recv_header = 1; + return -1; + } + + char* data_begin = strstr(encryptdata, "\r\n\r\n"); + if (data_begin) { + int outlength; + char *ret_buf = (char*)malloc(*capacity); + memset(ret_buf, 0x00, *capacity); + int ret_buf_len = 0; + ret_buf_len = get_data_from_http_header(encryptdata, &ret_buf); + + if (self->server.param && strlen(self->server.param) == 0) + { + self->server.param = NULL; + } + else + { + if(local->host_matched == 0) + { + char *host = (char*)malloc(1024); + get_host_from_http_header(local->recv_buffer, &host); + char hosts[1024]; + char * phost[128]; + int host_num = 0; + int pos = 0; + int is_match = 0; + char * body_buffer = NULL; + strncpy(hosts, self->server.param, sizeof hosts); + phost[host_num++] = hosts; + + for (pos = 0; hosts[pos]; ++pos) { + if (hosts[pos] == ',') { + phost[host_num++] = &hosts[pos + 1]; + hosts[pos] = 0; + } else if (hosts[pos] == '#') { + char * body_pointer = &hosts[pos + 1]; + char * p; + int trans_char = 0; + p = body_buffer = (char*)malloc(2048); + for ( ; *body_pointer; ++body_pointer) { + if (*body_pointer == '\\') { + trans_char = 1; + continue; + } else if (*body_pointer == '\n') { + *p = '\r'; + *++p = '\n'; + continue; + } + if (trans_char) { + if (*body_pointer == '\\' ) { + *p = '\\'; + } else if (*body_pointer == 'n' ) { + *p = '\r'; + *++p = '\n'; + } else { + *p = '\\'; + *p = *body_pointer; + } + trans_char = 0; + } else { + *p = *body_pointer; + } + ++p; + } + *p = 0; + hosts[pos] = 0; + break; + } + } + + + for(pos = 0; pos < host_num; pos++) + { + if(strcmp(phost[pos], host) == 0) + { + is_match = 1; + local->host_matched = 1; + } + } + + if(is_match == 0) + { + free(local->recv_buffer); + local->recv_buffer = malloc(0); + local->recv_buffer_size = 0; + local->has_sent_header = 1; + local->has_recv_header = 1; + LOGE("http_simple: not match host, host: %s", host); + return -1; + } + + free(host); + } + } + + if(ret_buf_len <= 0) + { + return -1; + } + + data_begin += 4; + local->has_recv_header = 1; + + ret_buf = (char*)realloc(ret_buf, ret_buf_len + datalength - (data_begin - encryptdata)); + outlength = ret_buf_len + datalength - (data_begin - encryptdata); + + memcpy(ret_buf + ret_buf_len, data_begin, datalength - (data_begin - encryptdata)); + + if (*capacity < outlength) { + *pencryptdata = (char*)realloc(*pencryptdata, *capacity = outlength * 2); + encryptdata = *pencryptdata; + } + + memcpy(encryptdata, ret_buf, outlength); + free(ret_buf); + return outlength; + } else { + return 0; + } +} + +void boundary(char result[]) +{ + char *str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + int i,lstr; + char ss[3] = {0}; + lstr = strlen(str); + srand((unsigned int)time((time_t *)NULL)); + for(i = 0; i < 32; ++i) + { + sprintf(ss, "%c", str[(rand()%lstr)]); + strcat(result, ss); + } +} + +int http_post_client_encode(obfs *self, char **pencryptdata, int datalength, size_t* capacity) { + char *encryptdata = *pencryptdata; + http_simple_local_data *local = (http_simple_local_data*)self->l_data; + if (local->has_sent_header) { + return datalength; + } + char hosts[1024]; + char * phost[128]; + int host_num = 0; + int pos; + char hostport[128]; + int head_size = self->server.head_len + (xorshift128plus() & 0x3F); + int outlength; + char * out_buffer = (char*)malloc(datalength + 2048); + char * body_buffer = NULL; + if (head_size > datalength) + head_size = datalength; + http_simple_encode_head(local, encryptdata, head_size); + if (self->server.param && strlen(self->server.param) == 0) + self->server.param = NULL; + strncpy(hosts, self->server.param ? self->server.param : self->server.host, sizeof hosts); + phost[host_num++] = hosts; + for (pos = 0; hosts[pos]; ++pos) { + if (hosts[pos] == ',') { + phost[host_num++] = &hosts[pos + 1]; + hosts[pos] = 0; + } else if (hosts[pos] == '#') { + char * body_pointer = &hosts[pos + 1]; + char * p; + int trans_char = 0; + p = body_buffer = (char*)malloc(2048); + for ( ; *body_pointer; ++body_pointer) { + if (*body_pointer == '\\') { + trans_char = 1; + continue; + } else if (*body_pointer == '\n') { + *p = '\r'; + *++p = '\n'; + continue; + } + if (trans_char) { + if (*body_pointer == '\\' ) { + *p = '\\'; + } else if (*body_pointer == 'n' ) { + *p = '\r'; + *++p = '\n'; + } else { + *p = '\\'; + *p = *body_pointer; + } + trans_char = 0; + } else { + *p = *body_pointer; + } + ++p; + } + *p = 0; + hosts[pos] = 0; + break; + } + } + host_num = xorshift128plus() % host_num; + if (self->server.port == 80) + sprintf(hostport, "%s", phost[host_num]); + else + sprintf(hostport, "%s:%d", phost[host_num], self->server.port); + if (body_buffer) { + sprintf(out_buffer, + "POST /%s HTTP/1.1\r\n" + "Host: %s\r\n" + "%s\r\n\r\n", + local->encode_buffer, + hostport, + body_buffer); + } else { + char result[33] = {0}; + boundary(result); + sprintf(out_buffer, + "POST /%s HTTP/1.1\r\n" + "Host: %s\r\n" + "User-Agent: %s\r\n" + "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n" + "Accept-Language: en-US,en;q=0.8\r\n" + "Accept-Encoding: gzip, deflate\r\n" + "Content-Type: multipart/form-data; boundary=%s\r\n" + "DNT: 1\r\n" + "Connection: keep-alive\r\n" + "\r\n", + local->encode_buffer, + hostport, + g_useragent[g_useragent_index], + result + ); + } + //LOGI("http header: %s", out_buffer); + outlength = strlen(out_buffer); + memmove(out_buffer + outlength, encryptdata + head_size, datalength - head_size); + outlength += datalength - head_size; + local->has_sent_header = 1; + if (*capacity < outlength) { + *pencryptdata = (char*)realloc(*pencryptdata, *capacity = outlength * 2); + encryptdata = *pencryptdata; + } + memmove(encryptdata, out_buffer, outlength); + free(out_buffer); + if (body_buffer != NULL) + free(body_buffer); + if (local->encode_buffer != NULL) { + free(local->encode_buffer); + local->encode_buffer = NULL; + } + return outlength; +} diff --git a/shadowsocksr-libev/src/server/http_simple.h b/shadowsocksr-libev/src/server/http_simple.h new file mode 100644 index 000000000..cce24cc17 --- /dev/null +++ b/shadowsocksr-libev/src/server/http_simple.h @@ -0,0 +1,21 @@ +/* + * http_simple.h - Define shadowsocksR server's buffers and callbacks + * + * Copyright (C) 2015 - 2016, Break Wa11 + */ + +#ifndef _HTTP_SIMPLE_H +#define _HTTP_SIMPLE_H + +obfs * http_simple_new_obfs(); +void http_simple_dispose(obfs *self); + +int http_simple_client_encode(obfs *self, char **pencryptdata, int datalength, size_t* capacity); +int http_simple_client_decode(obfs *self, char **pencryptdata, int datalength, size_t* capacity, int *needsendback); + +int http_post_client_encode(obfs *self, char **pencryptdata, int datalength, size_t* capacity); + +int http_simple_server_encode(obfs *self, char **pencryptdata, int datalength, size_t* capacity); +int http_simple_server_decode(obfs *self, char **pencryptdata, int datalength, size_t* capacity, int *needsendback); + +#endif // _HTTP_SIMPLE_H diff --git a/shadowsocksr-libev/src/server/jconf.c b/shadowsocksr-libev/src/server/jconf.c new file mode 100644 index 000000000..494aa5f4e --- /dev/null +++ b/shadowsocksr-libev/src/server/jconf.c @@ -0,0 +1,260 @@ +/* + * jconf.c - Parse the JSON format config file + * + * Copyright (C) 2013 - 2016, Max Lv + * + * This file is part of the shadowsocks-libev. + * shadowsocks-libev is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * shadowsocks-libev 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with shadowsocks-libev; see the file COPYING. If not, see + * . + */ + +#include +#include +#include +#include +#include + +#include "utils.h" +#include "jconf.h" +#include "json.h" +#include "string.h" + +#include + +#define check_json_value_type(value, expected_type, message) \ + do { \ + if ((value)->type != (expected_type)) \ + FATAL((message)); \ + } while(0) + +static char * +to_string(const json_value *value) +{ + if (value->type == json_string) { + return ss_strndup(value->u.string.ptr, value->u.string.length); + } else if (value->type == json_integer) { + return strdup(ss_itoa(value->u.integer)); + } else if (value->type == json_null) { + return "null"; + } else { + LOGE("%d", value->type); + FATAL("Invalid config format."); + } + return 0; +} + +void +free_addr(ss_addr_t *addr) +{ + ss_free(addr->host); + ss_free(addr->port); +} + +void +parse_addr(const char *str, ss_addr_t *addr) +{ + int ipv6 = 0, ret = -1, n = 0; + char *pch; + + struct cork_ip ip; + if (cork_ip_init(&ip, str) != -1) { + addr->host = strdup(str); + addr->port = NULL; + return; + } + + pch = strchr(str, ':'); + while (pch != NULL) { + n++; + ret = pch - str; + pch = strchr(pch + 1, ':'); + } + if (n > 1) { + ipv6 = 1; + if (str[ret - 1] != ']') { + ret = -1; + } + } + + if (ret == -1) { + if (ipv6) { + addr->host = ss_strndup(str + 1, strlen(str) - 2); + } else { + addr->host = strdup(str); + } + addr->port = NULL; + } else { + if (ipv6) { + addr->host = ss_strndup(str + 1, ret - 2); + } else { + addr->host = ss_strndup(str, ret); + } + addr->port = strdup(str + ret + 1); + } +} + +jconf_t * +read_jconf(const char *file) +{ + static jconf_t conf; + + memset(&conf, 0, sizeof(jconf_t)); + + char *buf; + json_value *obj; + + FILE *f = fopen(file, "rb"); + if (f == NULL) { + FATAL("Invalid config path."); + } + + fseek(f, 0, SEEK_END); + long pos = ftell(f); + fseek(f, 0, SEEK_SET); + + if (pos >= MAX_CONF_SIZE) { + FATAL("Too large config file."); + } + + buf = ss_malloc(pos + 1); + if (buf == NULL) { + FATAL("No enough memory."); + } + + int nread = fread(buf, pos, 1, f); + if (!nread) { + FATAL("Failed to read the config file."); + } + fclose(f); + + buf[pos] = '\0'; // end of string + + json_settings settings = { 0UL, 0, NULL, NULL, NULL }; + char error_buf[512]; + obj = json_parse_ex(&settings, buf, pos, error_buf); + + if (obj == NULL) { + FATAL(error_buf); + } + + if (obj->type == json_object) { + unsigned int i, j; + for (i = 0; i < obj->u.object.length; i++) { + char *name = obj->u.object.values[i].name; + json_value *value = obj->u.object.values[i].value; + if (strcmp(name, "server") == 0) { + if (value->type == json_array) { + for (j = 0; j < value->u.array.length; j++) { + if (j >= MAX_REMOTE_NUM) { + break; + } + json_value *v = value->u.array.values[j]; + char *addr_str = to_string(v); + parse_addr(addr_str, conf.remote_addr + j); + ss_free(addr_str); + conf.remote_num = j + 1; + } + } else if (value->type == json_string) { + conf.remote_addr[0].host = to_string(value); + conf.remote_addr[0].port = NULL; + conf.remote_num = 1; + } + } else if (strcmp(name, "port_password") == 0) { + if (value->type == json_object) { + for (j = 0; j < value->u.object.length; j++) { + if (j >= MAX_PORT_NUM) { + break; + } + json_value *v = value->u.object.values[j].value; + if (v->type == json_string) { + conf.port_password[j].port = ss_strndup(value->u.object.values[j].name, + value->u.object.values[j].name_length); + conf.port_password[j].password = to_string(v); + conf.port_password_num = j + 1; + } + } + } + } else if (strcmp(name, "server_port") == 0) { + conf.remote_port = to_string(value); + } else if (strcmp(name, "local_address") == 0) { + conf.local_addr = to_string(value); + } else if (strcmp(name, "local_port") == 0) { + conf.local_port = to_string(value); + } else if (strcmp(name, "password") == 0) { + conf.password = to_string(value); + } else if (strcmp(name, "protocol") == 0) { // SSR + conf.protocol = to_string(value); + } else if (strcmp(name, "protocol_param") == 0) { // SSR + conf.protocol_param = to_string(value); + } else if (strcmp(name, "method") == 0) { + conf.method = to_string(value); + } else if (strcmp(name, "obfs") == 0) { // SSR + conf.obfs = to_string(value); + } else if (strcmp(name, "obfs_param") == 0) { // SSR + conf.obfs_param = to_string(value); + } else if (strcmp(name, "timeout") == 0) { + conf.timeout = to_string(value); + } else if (strcmp(name, "user") == 0) { + conf.user = to_string(value); + } else if (strcmp(name, "fast_open") == 0) { + check_json_value_type(value, json_boolean, + "invalid config file: option 'fast_open' must be a boolean"); + conf.fast_open = value->u.boolean; + } else if (strcmp(name, "auth") == 0) { + check_json_value_type(value, json_boolean, + "invalid config file: option 'auth' must be a boolean"); + conf.auth = value->u.boolean; + } else if (strcmp(name, "nofile") == 0) { + check_json_value_type(value, json_integer, + "invalid config file: option 'nofile' must be an integer"); + conf.nofile = value->u.integer; + } else if (strcmp(name, "nameserver") == 0) { + conf.nameserver = to_string(value); + } else if (strcmp(name, "tunnel_address") == 0) { + conf.tunnel_address = to_string(value); + } else if (strcmp(name, "mode") == 0) { + char *mode_str = to_string(value); + + if (strcmp(mode_str, "tcp_only") == 0) + conf.mode = TCP_ONLY; + else if (strcmp(mode_str, "tcp_and_udp") == 0) + conf.mode = TCP_AND_UDP; + else if (strcmp(mode_str, "udp_only") == 0) + conf.mode = UDP_ONLY; + else + LOGI("ignore unknown mode: %s, use tcp_only as fallback", + mode_str); + ss_free(mode_str); + } else if (strcmp(name, "mtu") == 0) { + check_json_value_type(value, json_integer, + "invalid config file: option 'mtu' must be an integer"); + conf.mtu = value->u.integer; + } else if (strcmp(name, "mptcp") == 0) { + check_json_value_type(value, json_boolean, + "invalid config file: option 'mptcp' must be a boolean"); + conf.mptcp = value->u.boolean; + } else if (strcmp(name, "ipv6_first") == 0) { + check_json_value_type(value, json_boolean, + "invalid config file: option 'ipv6_first' must be a boolean"); + conf.ipv6_first = value->u.boolean; + } + } + } else { + FATAL("Invalid config file"); + } + + ss_free(buf); + json_value_free(obj); + return &conf; +} diff --git a/shadowsocksr-libev/src/server/jconf.h b/shadowsocksr-libev/src/server/jconf.h new file mode 100644 index 000000000..9a7e5e331 --- /dev/null +++ b/shadowsocksr-libev/src/server/jconf.h @@ -0,0 +1,78 @@ +/* + * jconf.h - Define the config data structure + * + * Copyright (C) 2013 - 2016, Max Lv + * + * This file is part of the shadowsocks-libev. + * shadowsocks-libev is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * shadowsocks-libev 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with shadowsocks-libev; see the file COPYING. If not, see + * . + */ + +#ifndef _JCONF_H +#define _JCONF_H + +#define MAX_PORT_NUM 1024 +#define MAX_REMOTE_NUM 10 +#define MAX_CONF_SIZE 128 * 1024 +#define MAX_DNS_NUM 4 +#define MAX_CONNECT_TIMEOUT 10 +#define MAX_REQUEST_TIMEOUT 60 +#define MIN_UDP_TIMEOUT 10 + +#define TCP_ONLY 0 +#define TCP_AND_UDP 1 +#define UDP_ONLY 3 + +typedef struct { + char *host; + char *port; +} ss_addr_t; + +typedef struct { + char *port; + char *password; +} ss_port_password_t; + +typedef struct { + int remote_num; + ss_addr_t remote_addr[MAX_REMOTE_NUM]; + int port_password_num; + ss_port_password_t port_password[MAX_PORT_NUM]; + char *remote_port; + char *local_addr; + char *local_port; + char *password; + char *protocol; // SSR + char *protocol_param; // SSR + char *method; + char *obfs; // SSR + char *obfs_param; // SSR + char *timeout; + char *user; + int auth; + int fast_open; + int nofile; + char *nameserver; + char *tunnel_address; + int mode; + int mtu; + int mptcp; + int ipv6_first; +} jconf_t; + +jconf_t *read_jconf(const char *file); +void parse_addr(const char *str, ss_addr_t *addr); +void free_addr(ss_addr_t *addr); + +#endif // _JCONF_H diff --git a/shadowsocksr-libev/src/server/json.c b/shadowsocksr-libev/src/server/json.c new file mode 100644 index 000000000..18e95ef7e --- /dev/null +++ b/shadowsocksr-libev/src/server/json.c @@ -0,0 +1,1002 @@ +/* vim: set et ts=3 sw=3 sts=3 ft=c: + * + * Copyright (C) 2012, 2013, 2014 James McLaughlin et al. All rights reserved. + * https://github.com/udp/json-parser + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "json.h" +#include "utils.h" + +#ifdef _MSC_VER +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS +#endif +#endif + +#ifdef __cplusplus +const struct _json_value json_value_none; /* zero-d by ctor */ +#else +const struct _json_value json_value_none = { NULL, 0, { 0 }, { NULL } }; +#endif + +#include +#include +#include +#include + +typedef unsigned short json_uchar; + +static unsigned char +hex_value(json_char c) +{ + if (isdigit((uint8_t)c)) { + return c - '0'; + } + + switch (c) { + case 'a': + case 'A': + return 0x0A; + case 'b': + case 'B': + return 0x0B; + case 'c': + case 'C': + return 0x0C; + case 'd': + case 'D': + return 0x0D; + case 'e': + case 'E': + return 0x0E; + case 'f': + case 'F': + return 0x0F; + default: + return 0xFF; + } +} + +typedef struct { + unsigned long used_memory; + + unsigned int uint_max; + unsigned long ulong_max; + + json_settings settings; + int first_pass; +} json_state; + +static void * +default_alloc(size_t size, int zero, void *user_data) +{ + return zero ? calloc(1, size) : ss_malloc(size); +} + +static void +default_free(void *ptr, void *user_data) +{ + ss_free(ptr); +} + +static void * +json_alloc(json_state *state, unsigned long size, int zero) +{ + if ((state->ulong_max - state->used_memory) < size) { + return 0; + } + + if (state->settings.max_memory + && (state->used_memory += size) > state->settings.max_memory) { + return 0; + } + + return state->settings.mem_alloc(size, zero, state->settings.user_data); +} + +static int +new_value(json_state *state, json_value **top, json_value **root, + json_value **alloc, json_type type) +{ + json_value *value; + int values_size; + + if (!state->first_pass) { + value = *top = *alloc; + *alloc = (*alloc)->_reserved.next_alloc; + + if (!*root) { + *root = value; + } + + switch (value->type) { + case json_array: + + if (!(value->u.array.values = (json_value **)json_alloc + (state, value->u.array.length * + sizeof(json_value *), 0))) { + return 0; + } + + value->u.array.length = 0; + break; + + case json_object: + + values_size = sizeof(*value->u.object.values) * + value->u.object.length; + + if (!((*(void **)&value->u.object.values) = json_alloc + (state, + values_size + + ((size_t)value->u. + object.values), + 0))) { + return 0; + } + + value->_reserved.object_mem = (*(char **)&value->u.object.values) + + values_size; + + value->u.object.length = 0; + break; + + case json_string: + + if (!(value->u.string.ptr = (json_char *)json_alloc + (state, + (value->u.string.length + + 1) * sizeof(json_char), 0))) { + return 0; + } + + value->u.string.length = 0; + break; + + default: + break; + } + + return 1; + } + + value = (json_value *)json_alloc(state, sizeof(json_value), 1); + + if (!value) { + return 0; + } + + if (!*root) { + *root = value; + } + + value->type = type; + value->parent = *top; + + if (*alloc) { + (*alloc)->_reserved.next_alloc = value; + } + + *alloc = *top = value; + + return 1; +} + +#define e_off \ + ((int)(i - cur_line_begin)) + +#define whitespace \ +case '\n': \ + ++cur_line; cur_line_begin = i; \ +case ' ': \ +case '\t': \ +case '\r' + +#define string_add(b) \ + do { if (!state.first_pass) { string[string_length] = b; \ + } ++string_length; } while (0) + +static const long + flag_next = 1 << 0, + flag_reproc = 1 << 1, + flag_need_comma = 1 << 2, + flag_seek_value = 1 << 3, + flag_escaped = 1 << 4, + flag_string = 1 << 5, + flag_need_colon = 1 << 6, + flag_done = 1 << 7, + flag_num_negative = 1 << 8, + flag_num_zero = 1 << 9, + flag_num_e = 1 << 10, + flag_num_e_got_sign = 1 << 11, + flag_num_e_negative = 1 << 12, + flag_line_comment = 1 << 13, + flag_block_comment = 1 << 14; + +json_value * +json_parse_ex(json_settings *settings, + const json_char *json, + size_t length, + char *error_buf) +{ + json_char error[json_error_max]; + int cur_line; + const json_char *cur_line_begin, *i, *end; + json_value *top, *root, *alloc = 0; + json_state state = { 0UL, 0U, 0UL, { 0UL, 0, NULL, NULL, NULL }, 0 }; + long flags; + long num_digits = 0, num_e = 0; + json_int_t num_fraction = 0; + + /* Skip UTF-8 BOM + */ + if (length >= 3 && ((unsigned char)json[0]) == 0xEF + && ((unsigned char)json[1]) == 0xBB + && ((unsigned char)json[2]) == 0xBF) { + json += 3; + length -= 3; + } + + error[0] = '\0'; + end = (json + length); + + memcpy(&state.settings, settings, sizeof(json_settings)); + + if (!state.settings.mem_alloc) { + state.settings.mem_alloc = default_alloc; + } + + if (!state.settings.mem_free) { + state.settings.mem_free = default_free; + } + + memset(&state.uint_max, 0xFF, sizeof(state.uint_max)); + memset(&state.ulong_max, 0xFF, sizeof(state.ulong_max)); + + state.uint_max -= 8; /* limit of how much can be added before next check */ + state.ulong_max -= 8; + + for (state.first_pass = 1; state.first_pass >= 0; --state.first_pass) { + json_uchar uchar; + unsigned char uc_b1, uc_b2, uc_b3, uc_b4; + json_char *string = 0; + unsigned int string_length = 0; + + top = root = 0; + flags = flag_seek_value; + + cur_line = 1; + cur_line_begin = json; + + for (i = json;; ++i) { + json_char b = (i == end ? 0 : *i); + + if (flags & flag_string) { + if (!b) { + sprintf(error, "Unexpected EOF in string (at %d:%d)", + cur_line, e_off); + goto e_failed; + } + + if (string_length > state.uint_max) { + goto e_overflow; + } + + if (flags & flag_escaped) { + flags &= ~flag_escaped; + + switch (b) { + case 'b': + string_add('\b'); + break; + case 'f': + string_add('\f'); + break; + case 'n': + string_add('\n'); + break; + case 'r': + string_add('\r'); + break; + case 't': + string_add('\t'); + break; + case 'u': + + if (end - i < 4 || + (uc_b1 = hex_value(*++i)) == 0xFF || + (uc_b2 = hex_value(*++i)) == 0xFF + || (uc_b3 = hex_value(*++i)) == 0xFF || + (uc_b4 = hex_value(*++i)) == 0xFF) { + sprintf(error, + "Invalid character value `%c` (at %d:%d)", + b, cur_line, e_off); + goto e_failed; + } + + uc_b1 = uc_b1 * 16 + uc_b2; + uc_b2 = uc_b3 * 16 + uc_b4; + + uchar = ((json_char)uc_b1) * 256 + uc_b2; + + if (sizeof(json_char) >= sizeof(json_uchar) || + (uc_b1 == 0 && uc_b2 <= 0x7F)) { + string_add((json_char)uchar); + break; + } + + if (uchar <= 0x7FF) { + if (state.first_pass) { + string_length += 2; + } else { + string[string_length++] = 0xC0 | + ((uc_b2 & + 0xC0) >> + 6) | + ((uc_b1 & 0x7) << 2); + string[string_length++] = 0x80 | + (uc_b2 & 0x3F); + } + + break; + } + + if (state.first_pass) { + string_length += 3; + } else { + string[string_length++] = 0xE0 | + ((uc_b1 & 0xF0) >> 4); + string[string_length++] = 0x80 | + ((uc_b1 & + 0xF) << + 2) | + ((uc_b2 & 0xC0) >> 6); + string[string_length++] = 0x80 | (uc_b2 & 0x3F); + } + + break; + + default: + string_add(b); + } + + continue; + } + + if (b == '\\') { + flags |= flag_escaped; + continue; + } + + if (b == '"') { + if (!state.first_pass) { + string[string_length] = 0; + } + + flags &= ~flag_string; + string = 0; + + switch (top->type) { + case json_string: + + top->u.string.length = string_length; + flags |= flag_next; + + break; + + case json_object: + + if (state.first_pass) { + (*(json_char **)&top->u.object.values) += + string_length + 1; + } else { + top->u.object.values[top->u.object.length].name + = (json_char *)top->_reserved.object_mem; + + top->u.object.values[top->u.object.length]. + name_length + = string_length; + + (*(json_char **)&top->_reserved.object_mem) += + string_length + 1; + } + + flags |= flag_seek_value | flag_need_colon; + continue; + + default: + break; + } + } else { + string_add(b); + continue; + } + } + + if (state.settings.settings & json_enable_comments) { + if (flags & (flag_line_comment | flag_block_comment)) { + if (flags & flag_line_comment) { + if (b == '\r' || b == '\n' || !b) { + flags &= ~flag_line_comment; + --i; /* so null can be reproc'd */ + } + + continue; + } + + if (flags & flag_block_comment) { + if (!b) { + sprintf(error, + "%d:%d: Unexpected EOF in block comment", + cur_line, e_off); + goto e_failed; + } + + if (b == '*' && i < (end - 1) && i[1] == '/') { + flags &= ~flag_block_comment; + ++i; /* skip closing sequence */ + } + + continue; + } + } else if (b == '/') { + if (!(flags & (flag_seek_value | flag_done)) && top->type != + json_object) { + sprintf(error, "%d:%d: Comment not allowed here", + cur_line, e_off); + goto e_failed; + } + + if (++i == end) { + sprintf(error, "%d:%d: EOF unexpected", cur_line, + e_off); + goto e_failed; + } + + switch (b = *i) { + case '/': + flags |= flag_line_comment; + continue; + + case '*': + flags |= flag_block_comment; + continue; + + default: + sprintf(error, + "%d:%d: Unexpected `%c` in comment opening sequence", cur_line, e_off, + b); + goto e_failed; + } + } + } + + if (flags & flag_done) { + if (!b) { + break; + } + + switch (b) { +whitespace: + continue; + + default: + sprintf(error, "%d:%d: Trailing garbage: `%c`", cur_line, + e_off, b); + goto e_failed; + } + } + + if (flags & flag_seek_value) { + switch (b) { +whitespace: + continue; + + case ']': + + if (top->type == json_array) { + flags = + (flags & + ~(flag_need_comma | flag_seek_value)) | flag_next; + } else { + sprintf(error, "%d:%d: Unexpected ]", cur_line, e_off); + goto e_failed; + } + + break; + + default: + + if (flags & flag_need_comma) { + if (b == ',') { + flags &= ~flag_need_comma; + continue; + } else { + sprintf(error, "%d:%d: Expected , before %c", + cur_line, e_off, b); + goto e_failed; + } + } + + if (flags & flag_need_colon) { + if (b == ':') { + flags &= ~flag_need_colon; + continue; + } else { + sprintf(error, "%d:%d: Expected : before %c", + cur_line, e_off, b); + goto e_failed; + } + } + + flags &= ~flag_seek_value; + + switch (b) { + case '{': + + if (!new_value(&state, &top, &root, &alloc, + json_object)) { + goto e_alloc_failure; + } + + continue; + + case '[': + + if (!new_value(&state, &top, &root, &alloc, + json_array)) { + goto e_alloc_failure; + } + + flags |= flag_seek_value; + continue; + + case '"': + + if (!new_value(&state, &top, &root, &alloc, + json_string)) { + goto e_alloc_failure; + } + + flags |= flag_string; + + string = top->u.string.ptr; + string_length = 0; + + continue; + + case 't': + + if ((end - i) < 3 || *(++i) != 'r' || *(++i) != 'u' || + *(++i) != 'e') { + goto e_unknown_value; + } + + if (!new_value(&state, &top, &root, &alloc, + json_boolean)) { + goto e_alloc_failure; + } + + top->u.boolean = 1; + + flags |= flag_next; + break; + + case 'f': + + if ((end - i) < 4 || *(++i) != 'a' || *(++i) != 'l' || + *(++i) != 's' || *(++i) != 'e') { + goto e_unknown_value; + } + + if (!new_value(&state, &top, &root, &alloc, + json_boolean)) { + goto e_alloc_failure; + } + + flags |= flag_next; + break; + + case 'n': + + if ((end - i) < 3 || *(++i) != 'u' || *(++i) != 'l' || + *(++i) != 'l') { + goto e_unknown_value; + } + + if (!new_value(&state, &top, &root, &alloc, + json_null)) { + goto e_alloc_failure; + } + + flags |= flag_next; + break; + + default: + + if (isdigit((uint8_t)b) || b == '-') { + if (!new_value(&state, &top, &root, &alloc, + json_integer)) { + goto e_alloc_failure; + } + + if (!state.first_pass) { + while (isdigit((uint8_t)b) || b == '+' || b == + '-' + || b == 'e' || b == 'E' || b == '.') { + if ((++i) == end) { + b = 0; + break; + } + + b = *i; + } + + flags |= flag_next | flag_reproc; + break; + } + + flags &= ~(flag_num_negative | flag_num_e | + flag_num_e_got_sign | + flag_num_e_negative | + flag_num_zero); + + num_digits = 0; + num_fraction = 0; + num_e = 0; + + if (b != '-') { + flags |= flag_reproc; + break; + } + + flags |= flag_num_negative; + continue; + } else { + sprintf(error, + "%d:%d: Unexpected %c when seeking value", + cur_line, e_off, b); + goto e_failed; + } + } + } + } else { + switch (top->type) { + case json_object: + + switch (b) { +whitespace: + continue; + + case '"': + + if (flags & flag_need_comma) { + sprintf(error, "%d:%d: Expected , before \"", + cur_line, e_off); + goto e_failed; + } + + flags |= flag_string; + + string = (json_char *)top->_reserved.object_mem; + string_length = 0; + + break; + + case '}': + + flags = (flags & ~flag_need_comma) | flag_next; + break; + + case ',': + + if (flags & flag_need_comma) { + flags &= ~flag_need_comma; + break; + } + + default: + + sprintf(error, "%d:%d: Unexpected `%c` in object", + cur_line, e_off, b); + goto e_failed; + } + + break; + + case json_integer: + case json_double: + + if (isdigit((uint8_t)b)) { + ++num_digits; + + if (top->type == json_integer || flags & flag_num_e) { + if (!(flags & flag_num_e)) { + if (flags & flag_num_zero) { + sprintf(error, + "%d:%d: Unexpected `0` before `%c`", + cur_line, e_off, b); + goto e_failed; + } + + if (num_digits == 1 && b == '0') { + flags |= flag_num_zero; + } + } else { + flags |= flag_num_e_got_sign; + num_e = (num_e * 10) + (b - '0'); + continue; + } + + top->u.integer = (top->u.integer * 10) + (b - '0'); + continue; + } + + num_fraction = (num_fraction * 10) + (b - '0'); + continue; + } + + if (b == '+' || b == '-') { + if ((flags & flag_num_e) && + !(flags & flag_num_e_got_sign)) { + flags |= flag_num_e_got_sign; + + if (b == '-') { + flags |= flag_num_e_negative; + } + + continue; + } + } else if (b == '.' && top->type == json_integer) { + if (!num_digits) { + sprintf(error, "%d:%d: Expected digit before `.`", + cur_line, e_off); + goto e_failed; + } + + top->type = json_double; + top->u.dbl = (double)top->u.integer; + + num_digits = 0; + continue; + } + + if (!(flags & flag_num_e)) { + if (top->type == json_double) { + if (!num_digits) { + sprintf(error, + "%d:%d: Expected digit after `.`", + cur_line, e_off); + goto e_failed; + } + + top->u.dbl += ((double)num_fraction) / + (pow(10, (double)num_digits)); + } + + if (b == 'e' || b == 'E') { + flags |= flag_num_e; + + if (top->type == json_integer) { + top->type = json_double; + top->u.dbl = (double)top->u.integer; + } + + num_digits = 0; + flags &= ~flag_num_zero; + + continue; + } + } else { + if (!num_digits) { + sprintf(error, "%d:%d: Expected digit after `e`", + cur_line, e_off); + goto e_failed; + } + + top->u.dbl *= + pow(10, + (double)((flags & + flag_num_e_negative) ? -num_e : num_e)); + } + + if (flags & flag_num_negative) { + if (top->type == json_integer) { + top->u.integer = -top->u.integer; + } else { + top->u.dbl = -top->u.dbl; + } + } + + flags |= flag_next | flag_reproc; + break; + + default: + break; + } + } + + if (flags & flag_reproc) { + flags &= ~flag_reproc; + --i; + } + + if (flags & flag_next) { + flags = (flags & ~flag_next) | flag_need_comma; + + if (!top->parent) { + /* root value done */ + + flags |= flag_done; + continue; + } + + if (top->parent->type == json_array) { + flags |= flag_seek_value; + } + + if (!state.first_pass) { + json_value *parent = top->parent; + + switch (parent->type) { + case json_object: + + parent->u.object.values + [parent->u.object.length].value = top; + + break; + + case json_array: + + parent->u.array.values + [parent->u.array.length] = top; + + break; + + default: + break; + } + } + + if ((++top->parent->u.array.length) > state.uint_max) { + goto e_overflow; + } + + top = top->parent; + + continue; + } + } + + alloc = root; + } + + return root; + +e_unknown_value: + + sprintf(error, "%d:%d: Unknown value", cur_line, e_off); + goto e_failed; + +e_alloc_failure: + + strcpy(error, "Memory allocation failure"); + goto e_failed; + +e_overflow: + + sprintf(error, "%d:%d: Too long (caught overflow)", cur_line, e_off); + goto e_failed; + +e_failed: + + if (error_buf) { + if (*error) { + strcpy(error_buf, error); + } else { + strcpy(error_buf, "Unknown error"); + } + } + + if (state.first_pass) { + alloc = root; + } + + while (alloc) { + top = alloc->_reserved.next_alloc; + state.settings.mem_free(alloc, state.settings.user_data); + alloc = top; + } + + if (!state.first_pass) { + json_value_free_ex(&state.settings, root); + } + + return 0; +} + +json_value * +json_parse(const json_char *json, size_t length) +{ + json_settings settings = { 0UL, 0, NULL, NULL, NULL }; + return json_parse_ex(&settings, json, length, 0); +} + +void +json_value_free_ex(json_settings *settings, json_value *value) +{ + json_value *cur_value; + + if (!value) { + return; + } + + value->parent = 0; + + while (value) { + switch (value->type) { + case json_array: + + if (!value->u.array.length) { + settings->mem_free(value->u.array.values, settings->user_data); + break; + } + + value = value->u.array.values[--value->u.array.length]; + continue; + + case json_object: + + if (!value->u.object.length) { + settings->mem_free(value->u.object.values, settings->user_data); + break; + } + + value = value->u.object.values[--value->u.object.length].value; + continue; + + case json_string: + + settings->mem_free(value->u.string.ptr, settings->user_data); + break; + + default: + break; + } + + cur_value = value; + value = value->parent; + settings->mem_free(cur_value, settings->user_data); + } +} + +void +json_value_free(json_value *value) +{ + json_settings settings = { 0UL, 0, NULL, NULL, NULL }; + settings.mem_free = default_free; + json_value_free_ex(&settings, value); +} diff --git a/shadowsocksr-libev/src/server/json.h b/shadowsocksr-libev/src/server/json.h new file mode 100644 index 000000000..016fc5a77 --- /dev/null +++ b/shadowsocksr-libev/src/server/json.h @@ -0,0 +1,249 @@ +/* vim: set et ts=3 sw=3 sts=3 ft=c: + * + * Copyright (C) 2012, 2013, 2014 James McLaughlin et al. All rights reserved. + * https://github.com/udp/json-parser + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _JSON_H +#define _JSON_H + +#ifndef json_char +#define json_char char +#endif + +#ifndef json_int_t +#ifndef _MSC_VER +#include +#define json_int_t int64_t +#else +#define json_int_t __int64 +#endif +#endif + +#include + +#ifdef __cplusplus + +#include + +extern "C" +{ +#endif + +typedef struct { + unsigned long max_memory; + int settings; + + /* Custom allocator support (leave null to use malloc/free) + */ + + void * (*mem_alloc)(size_t, int zero, void *user_data); + void (*mem_free)(void *, void *user_data); + + void *user_data; /* will be passed to mem_alloc and mem_free */ +} json_settings; + +#define json_enable_comments 0x01 + +typedef enum { + json_none, + json_object, + json_array, + json_integer, + json_double, + json_string, + json_boolean, + json_null +} json_type; + +extern const struct _json_value json_value_none; + +typedef struct _json_value { + struct _json_value *parent; + + json_type type; + + union { + int boolean; + json_int_t integer; + double dbl; + + struct { + unsigned int length; + json_char *ptr; /* null terminated */ + } string; + + struct { + unsigned int length; + + struct { + json_char *name; + unsigned int name_length; + + struct _json_value *value; + } *values; + +#if defined(__cplusplus) && __cplusplus >= 201103L + decltype(values) begin() const + { + return values; + } + decltype(values) end() const + { + return values + length; + } +#endif + } object; + + struct { + unsigned int length; + struct _json_value **values; + +#if defined(__cplusplus) && __cplusplus >= 201103L + decltype(values) begin() const + { + return values; + } + decltype(values) end() const + { + return values + length; + } +#endif + } array; + } u; + + union { + struct _json_value *next_alloc; + void *object_mem; + } _reserved; + + /* Some C++ operator sugar */ + +#ifdef __cplusplus + +public: + + inline _json_value(){ + memset(this, 0, sizeof(_json_value)); + } + + inline const struct _json_value &operator [] (int index) const { + if (type != json_array || index < 0 + || ((unsigned int)index) >= u.array.length) { + return json_value_none; + } + + return *u.array.values[index]; + } + + inline const struct _json_value &operator [] (const char *index) const { + if (type != json_object) { + return json_value_none; + } + + for (unsigned int i = 0; i < u.object.length; ++i) + if (!strcmp(u.object.values[i].name, index)) { + return *u.object.values[i].value; + } + + return json_value_none; + } + + inline operator const char * () const + { + switch (type) { + case json_string: + return u.string.ptr; + + default: + return ""; + } + } + + inline operator + json_int_t() const + { + switch (type) { + case json_integer: + return u.integer; + + case json_double: + return (json_int_t)u.dbl; + + default: + return 0; + } + } + + inline operator + bool() const + { + if (type != json_boolean) { + return false; + } + + return u.boolean != 0; + } + + inline operator double () const + { + switch (type) { + case json_integer: + return (double)u.integer; + + case json_double: + return u.dbl; + + default: + return 0; + } + } + +#endif +} json_value; + +json_value *json_parse(const json_char *json, + size_t length); + +#define json_error_max 128 +json_value *json_parse_ex(json_settings *settings, + const json_char *json, + size_t length, + char *error); + +void json_value_free(json_value *); + +/* Not usually necessary, unless you used a custom mem_alloc and now want to + * use a custom mem_free. + */ +void json_value_free_ex(json_settings *settings, + json_value *); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/shadowsocksr-libev/src/server/list.c b/shadowsocksr-libev/src/server/list.c new file mode 100644 index 000000000..dde085d84 --- /dev/null +++ b/shadowsocksr-libev/src/server/list.c @@ -0,0 +1,370 @@ +#include "list.h" + +/// 文件:list_impl.c +/// 功能:实现链表的基本操作 +/// 作者:bluewind +/// 完成时间:2011.5.29 +/// 修改时间:2011.5.31, 2011.7.2 +/// 修改备注:在头节点处添加一个空节点,可以优化添加、删除节点代码 +/// 再次修改,链表增加节点数据data_size,限制数据大小,修改了 +/// 添加复制数据代码,修正重复添加节点后释放节点的Bug,添加了前 +/// 插、排序和遍历功能,7.3 添加tail尾指针,改进后插法性能,并改名 +/// -------------------------------------------------------------- + +void swap_data(Node n1, Node n2); + +/// -------------------------------------------------------------- +// 函数名:list_init +// 功能: 链表初始化 +// 参数: 无 +// 返回值:已初始化链表指针 +// 备注: 链表本身动态分配,由list_destroy函数管理释放 +/// -------------------------------------------------------------- +List list_init(unsigned int data_size) +{ + List list = (List) malloc(sizeof(struct clist)); + if(list != NULL) //内存分配成功 + { + list->head = (Node) malloc(sizeof(node)); //为头节点分配内存 + if(list->head) //内存分配成功 + { + list->head->data = NULL; //初始化头节点 + list->head->next = NULL; + list->data_size = data_size; + list->tail = list->head; + list->size = 0; + + list->add_back = list_add_back; //初始化成员函数 + list->add_front = list_add_front; + list->delete_node = list_delete_node; + list->delete_at = list_delete_at; + list->modify_at = list_modify_at; + list->have_same = list_have_same; + list->have_same_cmp = list_have_same_cmp; + list->foreach = list_foreach; + list->clear = list_clear; + list->sort = list_sort; + list->destroy = list_destroy; + } + } + return list; +} + +/// -------------------------------------------------------------- +// 函数名:list_add_back +// 功能: 添加链表结点 (后插法) +// 参数: l--链表指针,data--链表数据指针,可为任意类型 +// 返回值:int型,为1表示添加成功,为0表示添加失败 +// 备注: 如果链表本身为空或是分配节点内存失败,将返回0 +/// -------------------------------------------------------------- +int list_add_back(List l, void *data) +{ + Node new_node = (Node) malloc(sizeof(node)); + + if(l != NULL && new_node != NULL) //链表本身不为空,且内存申请成功 + { + new_node->data = malloc(l->data_size); + memcpy(new_node->data, data, l->data_size); + new_node->next = NULL; + + l->tail->next = new_node; //添加节点 + l->tail = new_node; //记录尾节点位置 + l->size ++; //链表元素总数加1 + + return 1; + } + + return 0; +} + +/// -------------------------------------------------------------- +// 函数名:list_add_front +// 功能: 添加链表结点 (前插法) +// 参数: l--链表指针,data--链表数据指针,可为任意类型 +// 返回值:int型,为1表示添加成功,为0表示添加失败 +// 备注: 如果链表本身为空或是分配节点内存失败,将返回0 +/// -------------------------------------------------------------- +int list_add_front(List l, void *data) +{ + Node new_node = (Node) malloc(sizeof(node)); + + if(l != NULL && new_node != NULL) + { + new_node->data = malloc(l->data_size); + memcpy(new_node->data, data, l->data_size); + new_node->next = l->head->next; + + l->head->next = new_node; + if(!l->size) //记录尾指针位置 + l->tail = new_node; + l->size ++; + + return 1; + } + + return 0; +} + +/// -------------------------------------------------------------- +// 函数名:list_delete_node +// 功能:删除链表结点 +// 参数:l--链表指针,data--链表数据指针,可为任意类型 +// *pfunc为指向一个数据类型比较的函数指针 +// 返回值:int型,为1表示删除成功,为0表示没有找到匹配数据 +// 备注:*pfunc函数接口参数ndata为节点数据,data为比较数据,返回为真表示匹配数据 +/// -------------------------------------------------------------- +int list_delete_node(List l, void *data, int (*pfunc)(void *ndata, void *data)) +{ + if(l != NULL) + { + Node prev = l->head; //前一个节点 + Node curr = l->head->next; //当前节点 + + while(curr != NULL) + { + if(pfunc(curr->data, data)) //如果找到匹配数据 + { + if(curr == l->tail) //如果是删除尾节点 + l->tail = prev; + + prev->next = prev->next->next; //修改前节点next指针指向下下个节点 + + free(curr->data); //释放节点数据 + free(curr); //释放节点 + + l->size--; //链表元素总数减1 + return 1; //返回真值 + } + prev = prev->next; //没有找到匹配时移动前节点和当前节点 + curr = curr->next; + } + } + + return 0; //没有找到匹配数据 +} + +/// -------------------------------------------------------------- +// 函数名:list_delete_at +// 功能: 修改链表节点元素值 +// 参数: l--链表指针,index--索引值, 范围(0 -- size-1) +// 返回值:int型,为1表示删除成功,为0表示删除失败 +// 备注: 如果链表本身为空或是index为非法值,将返回0 +/// -------------------------------------------------------------- +int list_delete_at(List l, unsigned int index) +{ + unsigned int cindex = 0; + + if(l != NULL && index >= 0 && index < l->size) + { + Node prev = l->head; //前一个节点 + Node curr = l->head->next; //当前节点 + + while(cindex != index) + { + prev = prev->next; + curr = curr->next; + cindex ++; + } + + if(index == (l->size) - 1) + l->tail = prev; + + prev->next = prev->next->next; + free(curr->data); + free(curr); + l->size --; + + return 1; + } + + return 0; +} + +/// -------------------------------------------------------------- +// 函数名:list_modify_at +// 功能: 修改链表节点元素值 +// 参数: l--链表指针,index--索引值, 范围(0 -- size-1) +// data--链表数据指针 +// 返回值:int型,为1表示修改成功,为0表示修改失败 +// 备注: 如果链表本身为空或是index为非法值,将返回0 +/// -------------------------------------------------------------- +int list_modify_at(List l, unsigned int index, void *new_data) +{ + unsigned int cindex = 0; + + if(l != NULL && index >= 0 && index < l->size ) //非空链表,并且index值合法 + { + Node curr = l->head->next; + while(cindex != index) + { + curr = curr->next; + cindex ++; + } + memcpy(curr->data, new_data, l->data_size); + return 1; + } + + return 0; +} + +/// -------------------------------------------------------------- +// 函数名:list_sort +// 功能: 链表排序 +// 参数: l--链表指针,*pfunc为指向一个数据类型比较的函数指针 +// 返回值:无 +// 备注: 使用简单选择排序法,相比冒泡法每次交换,效率高一点 +/// -------------------------------------------------------------- +void list_sort(List l, compare pfunc) +{ + if(l != NULL) + { + Node min, icurr, jcurr; + + icurr = l->head->next; + while(icurr) + { + min = icurr; //记录最小值 + jcurr = icurr->next; //内循环指向下一个节点 + while(jcurr) + { + if(pfunc(min->data, jcurr->data)) //如果找到n+1到最后一个元素最小值 + min = jcurr; //记录下最小值的位置 + + jcurr = jcurr->next; + } + + if(min != icurr) //当最小值位置和n+1元素位置不相同时 + { + swap_data(min, icurr); //才进行交换,减少交换次数 + } + + icurr = icurr->next; + } + } +} + +void swap_data(Node n1, Node n2) +{ + void *temp; + + temp = n2->data; + n2->data = n1->data; + n1->data = temp; +} + + +int list_have_same(List l, void *data, int (*pfunc)(void *ndata, void *data)) +{ + if(l != NULL) + { + Node curr; + + for(curr = l->head->next; curr != NULL; curr = curr->next) + { + if(pfunc(curr->data, data)) + { + return 1; + } + } + } + + return 0; +} + +int list_have_same_cmp(List l, void *data) +{ + if(l != NULL) + { + Node curr; + + for(curr = l->head->next; curr != NULL; curr = curr->next) + { + if(memcmp(curr->data, data, l->data_size)) + { + return 1; + } + } + } + + return 0; +} + +/// -------------------------------------------------------------- +// 函数名:list_foreach +// 功能: 遍历链表元素 +// 参数: l--链表指针,doit为指向一个处理数据的函数指针 +// 返回值:无 +// 备注: doit申明为void (*dofunc)(void *ndata)原型 +/// -------------------------------------------------------------- +void list_foreach(List l, dofunc doit) +{ + if(l != NULL) + { + Node curr; + + for(curr = l->head->next; curr != NULL; curr = curr->next) + { + doit(curr->data); + } + } +} + +/// -------------------------------------------------------------- +// 函数名:list_clear +// 功能: 清空链表元素 +// 参数: l--链表指针 +// 返回值:无 +// 备注: 没有使用先Destroy再Init链表的办法,直接实现 +/// -------------------------------------------------------------- +void list_clear(List l) +{ + if(l != NULL) + { + Node temp; + Node curr = l->head->next; + + while(curr != NULL) + { + temp = curr->next; + + free(curr->data); //释放节点和数据 + free(curr); + + curr = temp; + } + + l->size = 0; //重置链表数据 + l->head->next = NULL; + l->tail = l->head; + } +} + +/// -------------------------------------------------------------- +// 函数名:list_destroy +// 功能: 释放链表 +// 参数: l--链表指针 +// 返回值:空链表指针 +/// -------------------------------------------------------------- +List list_destroy(List l) +{ + if(l != NULL) + { + Node temp; + + while(l->head) + { + temp = l->head->next; + + if(l->head->data != NULL) //如果是头节点就不释放数据空间 + free(l->head->data); //先释放节点数据(但是节点数据里也有指针?) + free(l->head); //再释放节点 + + l->head = temp; + } + + free(l); //释放链表本身占用空间 + l = NULL; + } + + return l; +} diff --git a/shadowsocksr-libev/src/server/list.h b/shadowsocksr-libev/src/server/list.h new file mode 100644 index 000000000..ab49720a2 --- /dev/null +++ b/shadowsocksr-libev/src/server/list.h @@ -0,0 +1,61 @@ +#ifndef LIST_H_H +#define LIST_H_H + +#include +#include +#include + +typedef struct clist *List; + +typedef int (*compare)(void *ndata, void *data); +typedef void (*dofunc)(void *ndata); + +typedef int (*lpf0)(List l, void *data); +typedef int (*lpf1)(List l, void *data, compare pfunc); +typedef List (*lpf2)(List l); +typedef void (*lpf3)(List l); +typedef void (*lpf4)(List l, dofunc pfunc); +typedef int (*lpf5)(List l, unsigned int index, void *new_data); +typedef void (*lpf6)(List l, compare pfunc); +typedef int (*lpf7)(List l, unsigned int index); + +typedef struct cnode +{ + void *data; + struct cnode *next; +}node, *Node; + +typedef struct clist +{ + Node head; + Node tail; + unsigned int size; + unsigned int data_size; + lpf0 add_back; + lpf0 add_front; + lpf1 delete_node; + lpf1 have_same; + lpf0 have_same_cmp; + lpf4 foreach; + lpf3 clear; + lpf2 destroy; + lpf5 modify_at; + lpf6 sort; + lpf7 delete_at; +}list; + +//初始化链表 +List list_init(unsigned int data_size); +int list_add_back(List l, void *data); +int list_add_front(List l, void *data); +int list_delete_node(List l, void *data, compare pfunc); +int list_delete_at(List l, unsigned int index); +int list_modify_at(List l, unsigned int index, void *new_data); +int list_have_same(List l, void *data, compare pfunc); +int list_have_same_cmp(List l, void *data); +void list_foreach(List l, dofunc doit); +void list_sort(List l, compare pfunc); +void list_clear(List l); +//释放链表 +List list_destroy(List l); +#endif diff --git a/shadowsocksr-libev/src/server/netutils.c b/shadowsocksr-libev/src/server/netutils.c new file mode 100644 index 000000000..3a32b4d0d --- /dev/null +++ b/shadowsocksr-libev/src/server/netutils.c @@ -0,0 +1,297 @@ +/* + * netutils.c - Network utilities + * + * Copyright (C) 2013 - 2016, Max Lv + * + * This file is part of the shadowsocks-libev. + * + * shadowsocks-libev is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * shadowsocks-libev 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with shadowsocks-libev; see the file COPYING. If not, see + * . + */ + +#include + +#include +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef __MINGW32__ +#include "win32.h" +#define sleep(n) Sleep(1000 * (n)) +#else +#include +#include +#include +#include +#endif + +#if defined(HAVE_SYS_IOCTL_H) && defined(HAVE_NET_IF_H) && defined(__linux__) +#include +#include +#define SET_INTERFACE +#endif + +#include "netutils.h" +#include "utils.h" + +#ifndef SO_REUSEPORT +#define SO_REUSEPORT 15 +#endif + +extern int verbose; + +static const char valid_label_bytes[] = + "-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz"; + +#if defined(MODULE_LOCAL) +extern int keep_resolving; +#endif + +int +set_reuseport(int socket) +{ + int opt = 1; + return setsockopt(socket, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt)); +} + +size_t +get_sockaddr_len(struct sockaddr *addr) +{ + if (addr->sa_family == AF_INET) { + return sizeof(struct sockaddr_in); + } else if (addr->sa_family == AF_INET6) { + return sizeof(struct sockaddr_in6); + } + return 0; +} + +#ifdef SET_INTERFACE +int +setinterface(int socket_fd, const char *interface_name) +{ + struct ifreq interface; + memset(&interface, 0, sizeof(struct ifreq)); + strncpy(interface.ifr_name, interface_name, IFNAMSIZ); + int res = setsockopt(socket_fd, SOL_SOCKET, SO_BINDTODEVICE, &interface, + sizeof(struct ifreq)); + return res; +} + +#endif + +int +bind_to_address(int socket_fd, const char *host) +{ + if (host != NULL) { + struct cork_ip ip; + struct sockaddr_storage storage; + memset(&storage, 0, sizeof(struct sockaddr_storage)); + if (cork_ip_init(&ip, host) != -1) { + if (ip.version == 4) { + struct sockaddr_in *addr = (struct sockaddr_in *)&storage; + dns_pton(AF_INET, host, &addr->sin_addr); + addr->sin_family = AF_INET; + return bind(socket_fd, (struct sockaddr *)addr, sizeof(struct sockaddr_in)); + } else if (ip.version == 6) { + struct sockaddr_in6 *addr = (struct sockaddr_in6 *)&storage; + dns_pton(AF_INET6, host, &addr->sin6_addr); + addr->sin6_family = AF_INET6; + return bind(socket_fd, (struct sockaddr *)addr, sizeof(struct sockaddr_in6)); + } + } + } + return -1; +} + +ssize_t +get_sockaddr(char *host, char *port, + struct sockaddr_storage *storage, int block, + int ipv6first) +{ + struct cork_ip ip; + if (cork_ip_init(&ip, host) != -1) { + if (ip.version == 4) { + struct sockaddr_in *addr = (struct sockaddr_in *)storage; + addr->sin_family = AF_INET; + dns_pton(AF_INET, host, &(addr->sin_addr)); + if (port != NULL) { + addr->sin_port = htons(atoi(port)); + } + } else if (ip.version == 6) { + struct sockaddr_in6 *addr = (struct sockaddr_in6 *)storage; + addr->sin6_family = AF_INET6; + dns_pton(AF_INET6, host, &(addr->sin6_addr)); + if (port != NULL) { + addr->sin6_port = htons(atoi(port)); + } + } + return 0; + } else { + struct addrinfo hints; + struct addrinfo *result, *rp; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; /* Return IPv4 and IPv6 choices */ + hints.ai_socktype = SOCK_STREAM; /* We want a TCP socket */ + + int err, i; + + for (i = 1; i < 8; i++) { + err = getaddrinfo(host, port, &hints, &result); +#if defined(MODULE_LOCAL) + if (!keep_resolving) + break; +#endif + if ((!block || !err)) { + break; + } else { + sleep(pow(2, i)); + LOGE("failed to resolve server name, wait %.0f seconds", pow(2, i)); + } + } + + if (err != 0) { + LOGE("getaddrinfo: %s", gai_strerror(err)); + return -1; + } + + int prefer_af = ipv6first ? AF_INET6 : AF_INET; + for (rp = result; rp != NULL; rp = rp->ai_next) + if (rp->ai_family == prefer_af) { + if (rp->ai_family == AF_INET) + memcpy(storage, rp->ai_addr, sizeof(struct sockaddr_in)); + else if (rp->ai_family == AF_INET6) + memcpy(storage, rp->ai_addr, sizeof(struct sockaddr_in6)); + break; + } + + if (rp == NULL) { + for (rp = result; rp != NULL; rp = rp->ai_next) { + if (rp->ai_family == AF_INET) + memcpy(storage, rp->ai_addr, sizeof(struct sockaddr_in)); + else if (rp->ai_family == AF_INET6) + memcpy(storage, rp->ai_addr, sizeof(struct sockaddr_in6)); + break; + } + } + + if (rp == NULL) { + LOGE("failed to resolve remote addr"); + return -1; + } + + freeaddrinfo(result); + return 0; + } + + return -1; +} + +int +sockaddr_cmp(struct sockaddr_storage *addr1, + struct sockaddr_storage *addr2, socklen_t len) +{ + struct sockaddr_in *p1_in = (struct sockaddr_in *)addr1; + struct sockaddr_in *p2_in = (struct sockaddr_in *)addr2; + struct sockaddr_in6 *p1_in6 = (struct sockaddr_in6 *)addr1; + struct sockaddr_in6 *p2_in6 = (struct sockaddr_in6 *)addr2; + if (p1_in->sin_family < p2_in->sin_family) + return -1; + if (p1_in->sin_family > p2_in->sin_family) + return 1; + /* compare ip4 */ + if (p1_in->sin_family == AF_INET) { + /* just order it, ntohs not required */ + if (p1_in->sin_port < p2_in->sin_port) + return -1; + if (p1_in->sin_port > p2_in->sin_port) + return 1; + return memcmp(&p1_in->sin_addr, &p2_in->sin_addr, INET_SIZE); + } else if (p1_in6->sin6_family == AF_INET6) { + /* just order it, ntohs not required */ + if (p1_in6->sin6_port < p2_in6->sin6_port) + return -1; + if (p1_in6->sin6_port > p2_in6->sin6_port) + return 1; + return memcmp(&p1_in6->sin6_addr, &p2_in6->sin6_addr, + INET6_SIZE); + } else { + /* eek unknown type, perform this comparison for sanity. */ + return memcmp(addr1, addr2, len); + } +} + +int +sockaddr_cmp_addr(struct sockaddr_storage *addr1, + struct sockaddr_storage *addr2, socklen_t len) +{ + struct sockaddr_in *p1_in = (struct sockaddr_in *)addr1; + struct sockaddr_in *p2_in = (struct sockaddr_in *)addr2; + struct sockaddr_in6 *p1_in6 = (struct sockaddr_in6 *)addr1; + struct sockaddr_in6 *p2_in6 = (struct sockaddr_in6 *)addr2; + if (p1_in->sin_family < p2_in->sin_family) + return -1; + if (p1_in->sin_family > p2_in->sin_family) + return 1; + /* compare ip4 */ + if (p1_in->sin_family == AF_INET) { + return memcmp(&p1_in->sin_addr, &p2_in->sin_addr, INET_SIZE); + } else if (p1_in6->sin6_family == AF_INET6) { + return memcmp(&p1_in6->sin6_addr, &p2_in6->sin6_addr, + INET6_SIZE); + } else { + /* eek unknown type, perform this comparison for sanity. */ + return memcmp(addr1, addr2, len); + } +} + +int +validate_hostname(const char *hostname, const int hostname_len) +{ + if (hostname == NULL) + return 0; + + if (hostname_len < 1 || hostname_len > 255) + return 0; + + if (hostname[0] == '.') + return 0; + + const char *label = hostname; + while (label < hostname + hostname_len) { + size_t label_len = hostname_len - (label - hostname); + char *next_dot = strchr(label, '.'); + if (next_dot != NULL) + label_len = next_dot - label; + + if (label + label_len > hostname + hostname_len) + return 0; + + if (label_len > 63 || label_len < 1) + return 0; + + if (label[0] == '-' || label[label_len - 1] == '-') + return 0; + + if (strspn(label, valid_label_bytes) < label_len) + return 0; + + label += label_len + 1; + } + + return 1; +} diff --git a/shadowsocksr-libev/src/server/netutils.h b/shadowsocksr-libev/src/server/netutils.h new file mode 100644 index 000000000..07255923e --- /dev/null +++ b/shadowsocksr-libev/src/server/netutils.h @@ -0,0 +1,98 @@ +/* + * netutils.h - Network utilities + * + * Copyright (C) 2013 - 2016, Max Lv + * + * This file is part of the shadowsocks-libev. + * + * shadowsocks-libev is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * shadowsocks-libev 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with shadowsocks-libev; see the file COPYING. If not, see + * . + */ + +#ifndef _NETUTILS_H +#define _NETUTILS_H + +#if defined(__linux__) +#include +#elif !defined(__MINGW32__) +#include +#endif + +// only enable TCP_FASTOPEN on linux +#if defined(__linux__) +#include +/* conditional define for TCP_FASTOPEN */ +#ifndef TCP_FASTOPEN +#define TCP_FASTOPEN 23 +#endif +/* conditional define for MSG_FASTOPEN */ +#ifndef MSG_FASTOPEN +#define MSG_FASTOPEN 0x20000000 +#endif +#elif !defined(__APPLE__) +#ifdef TCP_FASTOPEN +#undef TCP_FASTOPEN +#endif +#endif + +/* Backward compatibility for MPTCP_ENABLED between kernel 3 & 4 */ +#ifndef MPTCP_ENABLED +#ifdef TCP_CC_INFO +#define MPTCP_ENABLED 42 +#else +#define MPTCP_ENABLED 26 +#endif +#endif + +/** byte size of ip4 address */ +#define INET_SIZE 4 +/** byte size of ip6 address */ +#define INET6_SIZE 16 + +size_t get_sockaddr_len(struct sockaddr *addr); +ssize_t get_sockaddr(char *host, char *port, + struct sockaddr_storage *storage, int block, + int ipv6first); +int set_reuseport(int socket); + +#ifdef SET_INTERFACE +int setinterface(int socket_fd, const char *interface_name); +#endif + +int bind_to_address(int socket_fd, const char *address); + +/** + * Compare two sockaddrs. Imposes an ordering on the addresses. + * Compares address and port. + * @param addr1: address 1. + * @param addr2: address 2. + * @param len: lengths of addr. + * @return: 0 if addr1 == addr2. -1 if addr1 is smaller, +1 if larger. + */ +int sockaddr_cmp(struct sockaddr_storage *addr1, + struct sockaddr_storage *addr2, socklen_t len); + +/** + * Compare two sockaddrs. Compares address, not the port. + * @param addr1: address 1. + * @param addr2: address 2. + * @param len: lengths of addr. + * @return: 0 if addr1 == addr2. -1 if addr1 is smaller, +1 if larger. + */ +int sockaddr_cmp_addr(struct sockaddr_storage *addr1, + struct sockaddr_storage *addr2, socklen_t len); + +int validate_hostname(const char *hostname, const int hostname_len); + +#endif diff --git a/shadowsocksr-libev/src/server/obfs.c b/shadowsocksr-libev/src/server/obfs.c new file mode 100644 index 000000000..5c885bfef --- /dev/null +++ b/shadowsocksr-libev/src/server/obfs.c @@ -0,0 +1,205 @@ +#include +#include + +#include "utils.h" +#include "obfs.h" + +int rand_bytes(uint8_t *output, int len); +#define OBFS_HMAC_SHA1_LEN 10 + +#include "obfsutil.c" +#include "crc32.c" +#include "base64.c" +#include "http_simple.c" +#include "tls1.2_ticket.c" +#include "verify.c" +#include "auth.c" + +void * init_data() { + return malloc(1); +} + +obfs * new_obfs() { + obfs * self = (obfs*)malloc(sizeof(obfs)); + self->l_data = NULL; + return self; +} + +void set_server_info(obfs *self, server_info *server) { + memmove(&self->server, server, sizeof(server_info)); +} + +void get_server_info(obfs *self, server_info *server) { + memmove(server, &self->server, sizeof(server_info)); +} + +void dispose_obfs(obfs *self) { + free(self); +} + +obfs_class * new_obfs_class(char *plugin_name) +{ + if (plugin_name == NULL) + return NULL; + if (strcmp(plugin_name, "origin") == 0) + return NULL; + if (strcmp(plugin_name, "plain") == 0) + return NULL; + init_crc32_table(); + init_shift128plus(); + if (strcmp(plugin_name, "http_simple") == 0) { + obfs_class * plugin = (obfs_class*)malloc(sizeof(obfs)); + plugin->init_data = init_data; + plugin->new_obfs = http_simple_new_obfs; + plugin->get_server_info = get_server_info; + plugin->set_server_info = set_server_info; + plugin->dispose = http_simple_dispose; + + plugin->client_encode = http_simple_client_encode; + plugin->client_decode = http_simple_client_decode; + + plugin->server_encode = http_simple_server_encode; + plugin->server_decode = http_simple_server_decode; + + return plugin; + } else if (strcmp(plugin_name, "http_post") == 0) { + obfs_class * plugin = (obfs_class*)malloc(sizeof(obfs)); + plugin->init_data = init_data; + plugin->new_obfs = http_simple_new_obfs; + plugin->get_server_info = get_server_info; + plugin->set_server_info = set_server_info; + plugin->dispose = http_simple_dispose; + + plugin->client_encode = http_post_client_encode; + plugin->client_decode = http_simple_client_decode; + + plugin->server_encode = http_simple_server_encode; + plugin->server_decode = http_simple_server_decode; + + return plugin; + } else if (strcmp(plugin_name, "tls1.2_ticket_auth") == 0) { + obfs_class * plugin = (obfs_class*)malloc(sizeof(obfs)); + plugin->init_data = tls12_ticket_auth_init_data; + plugin->new_obfs = tls12_ticket_auth_new_obfs; + plugin->get_server_info = get_server_info; + plugin->set_server_info = set_server_info; + plugin->dispose = tls12_ticket_auth_dispose; + + plugin->client_encode = tls12_ticket_auth_client_encode; + plugin->client_decode = tls12_ticket_auth_client_decode; + + plugin->server_encode = tls12_ticket_auth_server_encode; + plugin->server_decode = tls12_ticket_auth_server_decode; + + return plugin; + } else if (strcmp(plugin_name, "verify_simple") == 0) { + obfs_class * plugin = (obfs_class*)malloc(sizeof(obfs)); + plugin->init_data = init_data; + plugin->new_obfs = verify_simple_new_obfs; + plugin->get_server_info = get_server_info; + plugin->set_server_info = set_server_info; + plugin->dispose = verify_simple_dispose; + + plugin->client_pre_encrypt = verify_simple_client_pre_encrypt; + plugin->client_post_decrypt = verify_simple_client_post_decrypt; + plugin->client_udp_pre_encrypt = NULL; + plugin->client_udp_post_decrypt = NULL; + + plugin->server_pre_encrypt = verify_simple_server_pre_encrypt; + plugin->server_post_decrypt = verify_simple_server_post_decrypt; + plugin->server_udp_pre_encrypt = NULL; + plugin->server_udp_post_decrypt = NULL; + + return plugin; + } else if (strcmp(plugin_name, "auth_simple") == 0) { + obfs_class * plugin = (obfs_class*)malloc(sizeof(obfs)); + plugin->init_data = auth_simple_init_data; + plugin->new_obfs = auth_simple_new_obfs; + plugin->get_server_info = get_server_info; + plugin->set_server_info = set_server_info; + plugin->dispose = auth_simple_dispose; + + plugin->client_pre_encrypt = auth_simple_client_pre_encrypt; + plugin->client_post_decrypt = auth_simple_client_post_decrypt; + plugin->client_udp_pre_encrypt = NULL; + plugin->client_udp_post_decrypt = NULL; + + return plugin; + } else if (strcmp(plugin_name, "auth_sha1") == 0) { + obfs_class * plugin = (obfs_class*)malloc(sizeof(obfs)); + plugin->init_data = auth_simple_init_data; + plugin->new_obfs = auth_simple_new_obfs; + plugin->get_server_info = get_server_info; + plugin->set_server_info = set_server_info; + plugin->dispose = auth_simple_dispose; + + plugin->client_pre_encrypt = auth_sha1_client_pre_encrypt; + plugin->client_post_decrypt = auth_sha1_client_post_decrypt; + plugin->client_udp_pre_encrypt = NULL; + plugin->client_udp_post_decrypt = NULL; + + return plugin; + } else if (strcmp(plugin_name, "auth_sha1_v2") == 0) { + obfs_class * plugin = (obfs_class*)malloc(sizeof(obfs)); + plugin->init_data = auth_simple_init_data; + plugin->new_obfs = auth_simple_new_obfs; + plugin->get_server_info = get_server_info; + plugin->set_server_info = set_server_info; + plugin->dispose = auth_simple_dispose; + + plugin->client_pre_encrypt = auth_sha1_v2_client_pre_encrypt; + plugin->client_post_decrypt = auth_sha1_v2_client_post_decrypt; + plugin->client_udp_pre_encrypt = NULL; + plugin->client_udp_post_decrypt = NULL; + + return plugin; + } else if (strcmp(plugin_name, "auth_sha1_v4") == 0) { + obfs_class * plugin = (obfs_class*)malloc(sizeof(obfs)); + plugin->init_data = auth_simple_init_data; + plugin->new_obfs = auth_simple_new_obfs; + plugin->get_server_info = get_server_info; + plugin->set_server_info = set_server_info; + plugin->dispose = auth_simple_dispose; + + plugin->client_pre_encrypt = auth_sha1_v4_client_pre_encrypt; + plugin->client_post_decrypt = auth_sha1_v4_client_post_decrypt; + plugin->client_udp_pre_encrypt = NULL; + plugin->client_udp_post_decrypt = NULL; + + return plugin; + } else if (strcmp(plugin_name, "auth_aes128_md5") == 0) { + obfs_class * plugin = (obfs_class*)malloc(sizeof(obfs)); + plugin->init_data = auth_simple_init_data; + plugin->new_obfs = auth_aes128_md5_new_obfs; + plugin->get_server_info = get_server_info; + plugin->set_server_info = set_server_info; + plugin->dispose = auth_simple_dispose; + + plugin->client_pre_encrypt = auth_aes128_sha1_client_pre_encrypt; + plugin->client_post_decrypt = auth_aes128_sha1_client_post_decrypt; + plugin->client_udp_pre_encrypt = auth_aes128_sha1_client_udp_pre_encrypt; + plugin->client_udp_post_decrypt = auth_aes128_sha1_client_udp_post_decrypt; + + return plugin; + } else if (strcmp(plugin_name, "auth_aes128_sha1") == 0) { + obfs_class * plugin = (obfs_class*)malloc(sizeof(obfs)); + plugin->init_data = auth_simple_init_data; + plugin->new_obfs = auth_aes128_sha1_new_obfs; + plugin->get_server_info = get_server_info; + plugin->set_server_info = set_server_info; + plugin->dispose = auth_simple_dispose; + + plugin->client_pre_encrypt = auth_aes128_sha1_client_pre_encrypt; + plugin->client_post_decrypt = auth_aes128_sha1_client_post_decrypt; + plugin->client_udp_pre_encrypt = auth_aes128_sha1_client_udp_pre_encrypt; + plugin->client_udp_post_decrypt = auth_aes128_sha1_client_udp_post_decrypt; + + return plugin; + } + LOGE("Load obfs '%s' failed", plugin_name); + return NULL; +} + +void free_obfs_class(obfs_class *plugin) { + free(plugin); +} diff --git a/shadowsocksr-libev/src/server/obfs.h b/shadowsocksr-libev/src/server/obfs.h new file mode 100644 index 000000000..74c60c9a2 --- /dev/null +++ b/shadowsocksr-libev/src/server/obfs.h @@ -0,0 +1,100 @@ +/* + * obfs.h - Define shadowsocksR server's buffers and callbacks + * + * Copyright (C) 2015 - 2016, Break Wa11 + */ + +#ifndef _OBFS_H +#define _OBFS_H + +#include +#include + +typedef struct server_info { + char host[64]; + uint16_t port; + char *param; + void *g_data; + uint8_t *iv; + size_t iv_len; + uint8_t *recv_iv; + size_t recv_iv_len; + uint8_t *key; + size_t key_len; + int head_len; + size_t tcp_mss; +}server_info; + +typedef struct obfs { + server_info server; + void *l_data; +}obfs; + +typedef struct obfs_class { + void * (*init_data)(); + obfs * (*new_obfs)(); + void (*get_server_info)(obfs *self, server_info *server); + void (*set_server_info)(obfs *self, server_info *server); + void (*dispose)(obfs *self); + + int (*client_pre_encrypt)(obfs *self, + char **pplaindata, + int datalength, + size_t* capacity); + int (*client_encode)(obfs *self, + char **pencryptdata, + int datalength, + size_t* capacity); + int (*client_decode)(obfs *self, + char **pencryptdata, + int datalength, + size_t* capacity, + int *needsendback); + int (*client_post_decrypt)(obfs *self, + char **pplaindata, + int datalength, + size_t* capacity); + int (*client_udp_pre_encrypt)(obfs *self, + char **pplaindata, + int datalength, + size_t* capacity); + int (*client_udp_post_decrypt)(obfs *self, + char **pplaindata, + int datalength, + size_t* capacity); + int (*server_pre_encrypt)(obfs *self, + char **pplaindata, + int datalength, + size_t* capacity); + int (*server_post_decrypt)(obfs *self, + char **pplaindata, + int datalength, + size_t* capacity); + int (*server_udp_pre_encrypt)(obfs *self, + char **pplaindata, + int datalength, + size_t* capacity); + int (*server_udp_post_decrypt)(obfs *self, + char **pplaindata, + int datalength, + size_t* capacity); + int (*server_encode)(obfs *self, + char **pencryptdata, + int datalength, + size_t* capacity); + int (*server_decode)(obfs *self, + char **pencryptdata, + int datalength, + size_t* capacity, + int *needsendback); +}obfs_class; + +obfs_class * new_obfs_class(char *plugin_name); +void free_obfs_class(obfs_class *plugin); + +void set_server_info(obfs *self, server_info *server); +void get_server_info(obfs *self, server_info *server); +obfs * new_obfs(); +void dispose_obfs(obfs *self); + +#endif // _OBFS_H diff --git a/shadowsocksr-libev/src/server/obfsutil.c b/shadowsocksr-libev/src/server/obfsutil.c new file mode 100644 index 000000000..d00959b9c --- /dev/null +++ b/shadowsocksr-libev/src/server/obfsutil.c @@ -0,0 +1,36 @@ +int get_head_size(char *plaindata, int size, int def_size) { + if (plaindata == NULL || size < 2) + return def_size; + int head_type = plaindata[0] & 0x7; + if (head_type == 1) + return 7; + if (head_type == 4) + return 19; + if (head_type == 3) + return 4 + plaindata[1]; + return def_size; +} + +static int shift128plus_init_flag = 0; +static uint64_t shift128plus_s[2] = {0x10000000, 0xFFFFFFFF}; + +void init_shift128plus(void) { + if (shift128plus_init_flag == 0) { + shift128plus_init_flag = 1; + uint32_t seed = time(NULL); + shift128plus_s[0] = seed | 0x100000000L; + shift128plus_s[1] = ((uint64_t)seed << 32) | 0x1; + } +} + +uint64_t xorshift128plus(void) { + uint64_t x = shift128plus_s[0]; + uint64_t const y = shift128plus_s[1]; + shift128plus_s[0] = y; + x ^= x << 23; // a + x ^= x >> 17; // b + x ^= y ^ (y >> 26); // c + shift128plus_s[1] = x; + return x + y; +} + diff --git a/shadowsocksr-libev/src/server/protocol.h b/shadowsocksr-libev/src/server/protocol.h new file mode 100644 index 000000000..eaa866e70 --- /dev/null +++ b/shadowsocksr-libev/src/server/protocol.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2014, Dustin Lundquist + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef PROTOCOL_H +#define PROTOCOL_H + +typedef struct protocol { + const int default_port; + int(*const parse_packet)(const char *, size_t, char **); +} protocol_t; + +#endif diff --git a/shadowsocksr-libev/src/server/resolv.c b/shadowsocksr-libev/src/server/resolv.c new file mode 100644 index 000000000..f580d06da --- /dev/null +++ b/shadowsocksr-libev/src/server/resolv.c @@ -0,0 +1,444 @@ +/* + * Copyright (c) 2014, Dustin Lundquist + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include + +#ifdef __MINGW32__ +#include "win32.h" +#else +#include +#include +#include +#include +#endif + +#include "resolv.h" +#include "utils.h" +#include "netutils.h" + +/* + * Implement DNS resolution interface using libudns + */ + +struct ResolvQuery { + void (*client_cb)(struct sockaddr *, void *); + void (*client_free_cb)(void *); + void *client_cb_data; + struct dns_query *queries[2]; + size_t response_count; + struct sockaddr **responses; + uint16_t port; +}; + +extern int verbose; + +static struct ev_io resolv_io_watcher; +static struct ev_timer resolv_timeout_watcher; +static const int MODE_IPV4_ONLY = 0; +static const int MODE_IPV6_ONLY = 1; +static const int MODE_IPV4_FIRST = 2; +static const int MODE_IPV6_FIRST = 3; +static int resolv_mode = 0; + +static void resolv_sock_cb(struct ev_loop *, struct ev_io *, int); +static void resolv_timeout_cb(struct ev_loop *, struct ev_timer *, int); +static void dns_query_v4_cb(struct dns_ctx *, struct dns_rr_a4 *, void *); +static void dns_query_v6_cb(struct dns_ctx *, struct dns_rr_a6 *, void *); +static void dns_timer_setup_cb(struct dns_ctx *, int, void *); +static void process_client_callback(struct ResolvQuery *); +static inline int all_queries_are_null(struct ResolvQuery *); +static struct sockaddr *choose_ipv4_first(struct ResolvQuery *); +static struct sockaddr *choose_ipv6_first(struct ResolvQuery *); +static struct sockaddr *choose_any(struct ResolvQuery *); + +int +resolv_init(struct ev_loop *loop, char **nameservers, int nameserver_num, int ipv6first) +{ + if (ipv6first) + resolv_mode = MODE_IPV6_FIRST; + else + resolv_mode = MODE_IPV4_FIRST; + + struct dns_ctx *ctx = &dns_defctx; + if (nameservers == NULL) { + /* Nameservers not specified, use system resolver config */ + dns_init(ctx, 0); + } else { + dns_reset(ctx); + + for (int i = 0; i < nameserver_num; i++) { + char *server = nameservers[i]; + dns_add_serv(ctx, server); + } + } + + int sockfd = dns_open(ctx); + if (sockfd < 0) { + FATAL("Failed to open DNS resolver socket"); + } + + if (nameserver_num == 1 && nameservers != NULL) { + if (strncmp("127.0.0.1", nameservers[0], 9) == 0 + || strncmp("::1", nameservers[0], 3) == 0) { + if (verbose) { + LOGI("bind UDP resolver to %s", nameservers[0]); + } + if (bind_to_address(sockfd, nameservers[0]) == -1) + ERROR("bind_to_address"); + } + } + +#ifdef __MINGW32__ + setnonblocking(sockfd); +#else + int flags = fcntl(sockfd, F_GETFL, 0); + fcntl(sockfd, F_SETFL, flags | O_NONBLOCK); +#endif + + ev_io_init(&resolv_io_watcher, resolv_sock_cb, sockfd, EV_READ); + resolv_io_watcher.data = ctx; + + ev_io_start(loop, &resolv_io_watcher); + + ev_timer_init(&resolv_timeout_watcher, resolv_timeout_cb, 0.0, 0.0); + resolv_timeout_watcher.data = ctx; + + dns_set_tmcbck(ctx, dns_timer_setup_cb, loop); + + return sockfd; +} + +void +resolv_shutdown(struct ev_loop *loop) +{ + struct dns_ctx *ctx = (struct dns_ctx *)resolv_io_watcher.data; + + ev_io_stop(loop, &resolv_io_watcher); + + if (ev_is_active(&resolv_timeout_watcher)) { + ev_timer_stop(loop, &resolv_timeout_watcher); + } + + dns_close(ctx); +} + +struct ResolvQuery * +resolv_query(const char *hostname, void (*client_cb)(struct sockaddr *, void *), + void (*client_free_cb)(void *), void *client_cb_data, + uint16_t port) +{ + struct dns_ctx *ctx = (struct dns_ctx *)resolv_io_watcher.data; + + /* + * Wrap udns's call back in our own + */ + struct ResolvQuery *cb_data = ss_malloc(sizeof(struct ResolvQuery)); + if (cb_data == NULL) { + LOGE("Failed to allocate memory for DNS query callback data."); + return NULL; + } + memset(cb_data, 0, sizeof(struct ResolvQuery)); + + cb_data->client_cb = client_cb; + cb_data->client_free_cb = client_free_cb; + cb_data->client_cb_data = client_cb_data; + memset(cb_data->queries, 0, sizeof(cb_data->queries)); + cb_data->response_count = 0; + cb_data->responses = NULL; + cb_data->port = port; + + /* Submit A and AAAA queries */ + if (resolv_mode != MODE_IPV6_ONLY) { + cb_data->queries[0] = dns_submit_a4(ctx, + hostname, 0, + dns_query_v4_cb, cb_data); + if (cb_data->queries[0] == NULL) { + LOGE("Failed to submit DNS query: %s", + dns_strerror(dns_status(ctx))); + } + } + + if (resolv_mode != MODE_IPV4_ONLY) { + cb_data->queries[1] = dns_submit_a6(ctx, + hostname, 0, + dns_query_v6_cb, cb_data); + if (cb_data->queries[1] == NULL) { + LOGE("Failed to submit DNS query: %s", + dns_strerror(dns_status(ctx))); + } + } + + if (all_queries_are_null(cb_data)) { + if (cb_data->client_free_cb != NULL) { + cb_data->client_free_cb(cb_data->client_cb_data); + } + ss_free(cb_data); + } + + return cb_data; +} + +void +resolv_cancel(struct ResolvQuery *query_handle) +{ + struct ResolvQuery *cb_data = (struct ResolvQuery *)query_handle; + struct dns_ctx *ctx = (struct dns_ctx *)resolv_io_watcher.data; + + for (int i = 0; i < sizeof(cb_data->queries) / sizeof(cb_data->queries[0]); + i++) + if (cb_data->queries[i] != NULL) { + dns_cancel(ctx, cb_data->queries[i]); + ss_free(cb_data->queries[i]); + } + + if (cb_data->client_free_cb != NULL) { + cb_data->client_free_cb(cb_data->client_cb_data); + } + + ss_free(cb_data); +} + +/* + * DNS UDP socket activity callback + */ +static void +resolv_sock_cb(struct ev_loop *loop, struct ev_io *w, int revents) +{ + struct dns_ctx *ctx = (struct dns_ctx *)w->data; + + if (revents & EV_READ) { + dns_ioevent(ctx, ev_now(loop)); + } +} + +/* + * Wrapper for client callback we provide to udns + */ +static void +dns_query_v4_cb(struct dns_ctx *ctx, struct dns_rr_a4 *result, void *data) +{ + struct ResolvQuery *cb_data = (struct ResolvQuery *)data; + + if (result == NULL) { + if (verbose) { + LOGI("IPv4 resolv: %s", dns_strerror(dns_status(ctx))); + } + } else if (result->dnsa4_nrr > 0) { + struct sockaddr **new_responses = ss_realloc(cb_data->responses, + (cb_data->response_count + + result->dnsa4_nrr) * + sizeof(struct sockaddr *)); + if (new_responses == NULL) { + LOGE("Failed to allocate memory for additional DNS responses"); + } else { + cb_data->responses = new_responses; + + for (int i = 0; i < result->dnsa4_nrr; i++) { + struct sockaddr_in *sa = + (struct sockaddr_in *)ss_malloc(sizeof(struct sockaddr_in)); + sa->sin_family = AF_INET; + sa->sin_port = cb_data->port; + sa->sin_addr = result->dnsa4_addr[i]; + + cb_data->responses[cb_data->response_count] = + (struct sockaddr *)sa; + if (cb_data->responses[cb_data->response_count] == NULL) { + LOGE( + "Failed to allocate memory for DNS query result address"); + } else { + cb_data->response_count++; + } + } + } + } + + ss_free(result); + cb_data->queries[0] = NULL; /* mark A query as being completed */ + + /* Once all queries have completed, call client callback */ + if (all_queries_are_null(cb_data)) { + return process_client_callback(cb_data); + } +} + +static void +dns_query_v6_cb(struct dns_ctx *ctx, struct dns_rr_a6 *result, void *data) +{ + struct ResolvQuery *cb_data = (struct ResolvQuery *)data; + + if (result == NULL) { + if (verbose) { + LOGI("IPv6 resolv: %s", dns_strerror(dns_status(ctx))); + } + } else if (result->dnsa6_nrr > 0) { + struct sockaddr **new_responses = ss_realloc(cb_data->responses, + (cb_data->response_count + + result->dnsa6_nrr) * + sizeof(struct sockaddr *)); + if (new_responses == NULL) { + LOGE("Failed to allocate memory for additional DNS responses"); + } else { + cb_data->responses = new_responses; + + for (int i = 0; i < result->dnsa6_nrr; i++) { + struct sockaddr_in6 *sa = + (struct sockaddr_in6 *)ss_malloc(sizeof(struct sockaddr_in6)); + sa->sin6_family = AF_INET6; + sa->sin6_port = cb_data->port; + sa->sin6_addr = result->dnsa6_addr[i]; + + cb_data->responses[cb_data->response_count] = + (struct sockaddr *)sa; + if (cb_data->responses[cb_data->response_count] == NULL) { + LOGE( + "Failed to allocate memory for DNS query result address"); + } else { + cb_data->response_count++; + } + } + } + } + + ss_free(result); + cb_data->queries[1] = NULL; /* mark AAAA query as being completed */ + + /* Once all queries have completed, call client callback */ + if (all_queries_are_null(cb_data)) { + return process_client_callback(cb_data); + } +} + +/* + * Called once all queries have been completed + */ +static void +process_client_callback(struct ResolvQuery *cb_data) +{ + struct sockaddr *best_address = NULL; + + if (resolv_mode == MODE_IPV4_FIRST) { + best_address = choose_ipv4_first(cb_data); + } else if (resolv_mode == MODE_IPV6_FIRST) { + best_address = choose_ipv6_first(cb_data); + } else { + best_address = choose_any(cb_data); + } + + cb_data->client_cb(best_address, cb_data->client_cb_data); + + for (int i = 0; i < cb_data->response_count; i++) + ss_free(cb_data->responses[i]); + + ss_free(cb_data->responses); + if (cb_data->client_free_cb != NULL) { + cb_data->client_free_cb(cb_data->client_cb_data); + } + ss_free(cb_data); +} + +static struct sockaddr * +choose_ipv4_first(struct ResolvQuery *cb_data) +{ + for (int i = 0; i < cb_data->response_count; i++) + if (cb_data->responses[i]->sa_family == AF_INET) { + return cb_data->responses[i]; + } + + return choose_any(cb_data); +} + +static struct sockaddr * +choose_ipv6_first(struct ResolvQuery *cb_data) +{ + for (int i = 0; i < cb_data->response_count; i++) + if (cb_data->responses[i]->sa_family == AF_INET6) { + return cb_data->responses[i]; + } + + return choose_any(cb_data); +} + +static struct sockaddr * +choose_any(struct ResolvQuery *cb_data) +{ + if (cb_data->response_count >= 1) { + return cb_data->responses[0]; + } + + return NULL; +} + +/* + * DNS timeout callback + */ +static void +resolv_timeout_cb(struct ev_loop *loop, struct ev_timer *w, int revents) +{ + struct dns_ctx *ctx = (struct dns_ctx *)w->data; + + if (revents & EV_TIMER) { + dns_timeouts(ctx, 30, ev_now(loop)); + } +} + +/* + * Callback to setup DNS timeout callback + */ +static void +dns_timer_setup_cb(struct dns_ctx *ctx, int timeout, void *data) +{ + struct ev_loop *loop = (struct ev_loop *)data; + + if (ev_is_active(&resolv_timeout_watcher)) { + ev_timer_stop(loop, &resolv_timeout_watcher); + } + + if (ctx != NULL && timeout >= 0) { + ev_timer_set(&resolv_timeout_watcher, timeout, 0.0); + ev_timer_start(loop, &resolv_timeout_watcher); + } +} + +static inline int +all_queries_are_null(struct ResolvQuery *cb_data) +{ + int result = 1; + + for (int i = 0; i < sizeof(cb_data->queries) / sizeof(cb_data->queries[0]); + i++) + result = result && cb_data->queries[i] == NULL; + + return result; +} diff --git a/shadowsocksr-libev/src/server/resolv.h b/shadowsocksr-libev/src/server/resolv.h new file mode 100644 index 000000000..055292233 --- /dev/null +++ b/shadowsocksr-libev/src/server/resolv.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2014, Dustin Lundquist + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef RESOLV_H +#define RESOLV_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#ifdef __MINGW32__ +#include "win32.h" +#else +#include +#endif + +struct ResolvQuery; + +int resolv_init(struct ev_loop *, char **, int, int); +struct ResolvQuery *resolv_query(const char *, void (*)(struct sockaddr *, + void *), void (*)( + void *), void *, uint16_t); +void resolv_cancel(struct ResolvQuery *); +void resolv_shutdown(struct ev_loop *); + +#endif diff --git a/shadowsocksr-libev/src/server/rule.c b/shadowsocksr-libev/src/server/rule.c new file mode 100644 index 000000000..41ba5e7d9 --- /dev/null +++ b/shadowsocksr-libev/src/server/rule.c @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2011 and 2012, Dustin Lundquist + * Copyright (c) 2011 Manuel Kasper + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#ifdef __MINGW32__ +extern void ss_error(const char *s); +#endif + +#include "rule.h" +#include "utils.h" + +static void free_rule(rule_t *); + +rule_t * +new_rule() +{ + rule_t *rule; + + rule = calloc(1, sizeof(rule_t)); + if (rule == NULL) { + ERROR("malloc"); + return NULL; + } + + return rule; +} + +int +accept_rule_arg(rule_t *rule, const char *arg) +{ + if (rule->pattern == NULL) { + rule->pattern = strdup(arg); + if (rule->pattern == NULL) { + ERROR("strdup failed"); + return -1; + } + } else { + LOGE("Unexpected table rule argument: %s", arg); + return -1; + } + + return 1; +} + +void +add_rule(struct cork_dllist *rules, rule_t *rule) +{ + cork_dllist_add(rules, &rule->entries); +} + +int +init_rule(rule_t *rule) +{ + if (rule->pattern_re == NULL) { + int errornumber; + PCRE2_SIZE erroroffset; + rule->pattern_re = pcre2_compile( + (PCRE2_SPTR)rule->pattern, /* the pattern */ + PCRE2_ZERO_TERMINATED, /* indicates pattern is zero-terminated */ + 0, /* default options */ + &errornumber, /* for error number */ + &erroroffset, /* for error offset */ + NULL); /* use default compile context */ + + if (rule->pattern_re == NULL) { + PCRE2_UCHAR errbuffer[512]; + pcre2_get_error_message(errornumber, errbuffer, sizeof(errbuffer)); + LOGE("PCRE2 regex compilation failed at offset %d: %s\n", (int)erroroffset, + errbuffer); + return 0; + } + + rule->pattern_re_match_data = pcre2_match_data_create_from_pattern(rule->pattern_re, NULL); + + if (rule->pattern_re_match_data == NULL) { + ERROR("PCRE2: the memory for the block could not be obtained"); + return 0; + } + } + + return 1; +} + +rule_t * +lookup_rule(const struct cork_dllist *rules, const char *name, size_t name_len) +{ + struct cork_dllist_item *curr, *next; + + if (name == NULL) { + name = ""; + name_len = 0; + } + + cork_dllist_foreach_void(rules, curr, next) { + rule_t *rule = cork_container_of(curr, rule_t, entries); + if (pcre2_match( + rule->pattern_re, /* the compiled pattern */ + (PCRE2_SPTR)name, /* the subject string */ + name_len, /* the length of the subject */ + 0, /* start at offset 0 in the subject */ + 0, /* default options */ + rule->pattern_re_match_data, /* block for storing the result */ + NULL /* use default match context */ + ) >= 0) + return rule; + } + + return NULL; +} + +void +remove_rule(rule_t *rule) +{ + cork_dllist_remove(&rule->entries); + free_rule(rule); +} + +static void +free_rule(rule_t *rule) +{ + if (rule == NULL) + return; + + ss_free(rule->pattern); + if (rule->pattern_re != NULL) { + pcre2_code_free(rule->pattern_re); /* data and the compiled pattern. */ + rule->pattern_re = NULL; + } + if (rule->pattern_re_match_data != NULL) { + pcre2_match_data_free(rule->pattern_re_match_data); /* Release memory used for the match */ + rule->pattern_re_match_data = NULL; + } + ss_free(rule); +} diff --git a/shadowsocksr-libev/src/server/rule.h b/shadowsocksr-libev/src/server/rule.h new file mode 100644 index 000000000..84a89af67 --- /dev/null +++ b/shadowsocksr-libev/src/server/rule.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2011 and 2012, Dustin Lundquist + * Copyright (c) 2011 Manuel Kasper + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef RULE_H +#define RULE_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +/* + * The PCRE2_CODE_UNIT_WIDTH macro must be defined before including pcre2.h. + * For a program that uses only one code unit width, setting it to 8, 16, or 32 + * makes it possible to use generic function names such as pcre2_compile(). Note + * that just changing 8 to 16 (for example) is not sufficient to convert this + * program to process 16-bit characters. Even in a fully 16-bit environment, where + * string-handling functions such as strcmp() and printf() work with 16-bit + * characters, the code for handling the table of named substrings will still need + * to be modified. + */ +/* we only need to support ASCII chartable, thus set it to 8 */ +#define PCRE2_CODE_UNIT_WIDTH 8 + +#include + +typedef struct rule { + char *pattern; + + /* Runtime fields */ + pcre2_code *pattern_re; + pcre2_match_data *pattern_re_match_data; + + struct cork_dllist_item entries; +} rule_t; + +void add_rule(struct cork_dllist *, rule_t *); +int init_rule(rule_t *); +rule_t *lookup_rule(const struct cork_dllist *, const char *, size_t); +void remove_rule(rule_t *); +rule_t *new_rule(); +int accept_rule_arg(rule_t *, const char *); + +#endif diff --git a/shadowsocksr-libev/src/server/server.c b/shadowsocksr-libev/src/server/server.c new file mode 100644 index 000000000..65b0e42a8 --- /dev/null +++ b/shadowsocksr-libev/src/server/server.c @@ -0,0 +1,2209 @@ +/* + * server.c - Provide shadowsocks service + * + * Copyright (C) 2013 - 2016, Max Lv + * + * This file is part of the shadowsocks-libev. + * + * shadowsocks-libev is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * shadowsocks-libev 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with shadowsocks-libev; see the file COPYING. If not, see + * . + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef __MINGW32__ +#include +#include +#include +#include +#include +#include +#endif + +#include +#include + +#ifdef __MINGW32__ +#include "win32.h" +#endif + +#if defined(HAVE_SYS_IOCTL_H) && defined(HAVE_NET_IF_H) && defined(__linux__) +#include +#include +#define SET_INTERFACE +#endif + +#include "netutils.h" +#include "utils.h" +#include "acl.h" +#include "server.h" + +#include "obfs.c" // I don't want to modify makefile + +#ifndef EAGAIN +#define EAGAIN EWOULDBLOCK +#endif + +#ifndef EWOULDBLOCK +#define EWOULDBLOCK EAGAIN +#endif + +#ifndef BUF_SIZE +#define BUF_SIZE 2048 +#endif + +#ifndef SSMAXCONN +#define SSMAXCONN 1024 +#endif + +#ifndef UPDATE_INTERVAL +#define UPDATE_INTERVAL 30 +#endif + +static void signal_cb(EV_P_ ev_signal *w, int revents); +static void accept_cb(EV_P_ ev_io *w, int revents); +static void server_send_cb(EV_P_ ev_io *w, int revents); +static void server_recv_cb(EV_P_ ev_io *w, int revents); +static void remote_recv_cb(EV_P_ ev_io *w, int revents); +static void remote_send_cb(EV_P_ ev_io *w, int revents); +static void server_timeout_cb(EV_P_ ev_timer *watcher, int revents); +static void block_list_clear_cb(EV_P_ ev_timer *watcher, int revents); + +static remote_t *new_remote(int fd); +static server_t *new_server(int fd, listen_ctx_t *listener); +static remote_t *connect_to_remote(EV_P_ struct addrinfo *res, + server_t *server); + +static void free_remote(remote_t *remote); +static void close_and_free_remote(EV_P_ remote_t *remote); +static void free_server(server_t *server); +static void close_and_free_server(EV_P_ server_t *server); +static void server_resolve_cb(struct sockaddr *addr, void *data); +static void query_free_cb(void *data); + +static size_t parse_header_len(const char atyp, const char *data, size_t offset); +static int is_header_complete(const buffer_t *buf); + +int verbose = 0; + +static int acl = 0; +static int mode = TCP_ONLY; +static int auth = 0; +static int ipv6first = 0; + +static int protocol_compatible = 0;//SSR +static int obfs_compatible = 0;//SSR + +static int fast_open = 0; +#ifdef HAVE_SETRLIMIT +static int nofile = 0; +#endif +static int remote_conn = 0; +static int server_conn = 0; + +static char *bind_address = NULL; +static char *server_port = NULL; +static char *manager_address = NULL; +uint64_t tx = 0; +uint64_t rx = 0; +ev_timer stat_update_watcher; +ev_timer block_list_watcher; + +static struct cork_dllist connections; + +static void +stat_update_cb(EV_P_ ev_timer *watcher, int revents) +{ + struct sockaddr_un svaddr, claddr; + int sfd = -1; + size_t msgLen; + char resp[BUF_SIZE]; + + if (verbose) { + LOGI("update traffic stat: tx: %" PRIu64 " rx: %" PRIu64 "", tx, rx); + } + + snprintf(resp, BUF_SIZE, "stat: {\"%s\":%" PRIu64 "}", server_port, tx + rx); + msgLen = strlen(resp) + 1; + + ss_addr_t ip_addr = { .host = NULL, .port = NULL }; + parse_addr(manager_address, &ip_addr); + + if (ip_addr.host == NULL || ip_addr.port == NULL) { + sfd = socket(AF_UNIX, SOCK_DGRAM, 0); + if (sfd == -1) { + ERROR("stat_socket"); + return; + } + + memset(&claddr, 0, sizeof(struct sockaddr_un)); + claddr.sun_family = AF_UNIX; + snprintf(claddr.sun_path, sizeof(claddr.sun_path), "/tmp/shadowsocks.%s", server_port); + + unlink(claddr.sun_path); + + if (bind(sfd, (struct sockaddr *)&claddr, sizeof(struct sockaddr_un)) == -1) { + ERROR("stat_bind"); + close(sfd); + return; + } + + memset(&svaddr, 0, sizeof(struct sockaddr_un)); + svaddr.sun_family = AF_UNIX; + strncpy(svaddr.sun_path, manager_address, sizeof(svaddr.sun_path) - 1); + + if (sendto(sfd, resp, strlen(resp) + 1, 0, (struct sockaddr *)&svaddr, + sizeof(struct sockaddr_un)) != msgLen) { + ERROR("stat_sendto"); + close(sfd); + return; + } + + unlink(claddr.sun_path); + } else { + struct sockaddr_storage storage; + memset(&storage, 0, sizeof(struct sockaddr_storage)); + if (get_sockaddr(ip_addr.host, ip_addr.port, &storage, 0, ipv6first) == -1) { + ERROR("failed to parse the manager addr"); + return; + } + + sfd = socket(storage.ss_family, SOCK_DGRAM, 0); + + if (sfd == -1) { + ERROR("stat_socket"); + return; + } + + size_t addr_len = get_sockaddr_len((struct sockaddr *)&storage); + if (sendto(sfd, resp, strlen(resp) + 1, 0, (struct sockaddr *)&storage, + addr_len) != msgLen) { + ERROR("stat_sendto"); + close(sfd); + return; + } + } + + close(sfd); +} + +static void +free_connections(struct ev_loop *loop) +{ + struct cork_dllist_item *curr, *next; + cork_dllist_foreach_void(&connections, curr, next) { + server_t *server = cork_container_of(curr, server_t, entries); + remote_t *remote = server->remote; + close_and_free_server(loop, server); + close_and_free_remote(loop, remote); + } +} + +static size_t +parse_header_len(const char atyp, const char *data, size_t offset) +{ + size_t len = 0; + if ((atyp & ADDRTYPE_MASK) == 1) { + // IP V4 + len += sizeof(struct in_addr); + } else if ((atyp & ADDRTYPE_MASK) == 3) { + // Domain name + uint8_t name_len = *(uint8_t *)(data + offset); + len += name_len + 1; + } else if ((atyp & ADDRTYPE_MASK) == 4) { + // IP V6 + len += sizeof(struct in6_addr); + } else { + return 0; + } + len += 2; + return len; +} + +static int +is_header_complete(const buffer_t *buf) +{ + size_t header_len = 0; + size_t buf_len = buf->len; + + char atyp = buf->array[header_len]; + + // 1 byte for atyp + header_len++; + + if ((atyp & ADDRTYPE_MASK) == 1) { + // IP V4 + header_len += sizeof(struct in_addr); + } else if ((atyp & ADDRTYPE_MASK) == 3) { + // Domain name + // domain len + len of domain + if (buf_len < header_len + 1) + return 0; + uint8_t name_len = *(uint8_t *)(buf->array + header_len); + header_len += name_len + 1; + } else if ((atyp & ADDRTYPE_MASK) == 4) { + // IP V6 + header_len += sizeof(struct in6_addr); + } else { + return -1; + } + + // len of port + header_len += 2; + + // size of ONETIMEAUTH_BYTES + if (auth || (atyp & ONETIMEAUTH_FLAG)) { + header_len += ONETIMEAUTH_BYTES; + } + + return buf_len >= header_len ? 1 : 0; +} + +static char * +get_peer_name(int fd) +{ + static char peer_name[INET6_ADDRSTRLEN] = { 0 }; + struct sockaddr_storage addr; + socklen_t len = sizeof(struct sockaddr_storage); + memset(&addr, 0, len); + memset(peer_name, 0, INET6_ADDRSTRLEN); + int err = getpeername(fd, (struct sockaddr *)&addr, &len); + if (err == 0) { + if (addr.ss_family == AF_INET) { + struct sockaddr_in *s = (struct sockaddr_in *)&addr; + dns_ntop(AF_INET, &s->sin_addr, peer_name, INET_ADDRSTRLEN); + } else if (addr.ss_family == AF_INET6) { + struct sockaddr_in6 *s = (struct sockaddr_in6 *)&addr; + dns_ntop(AF_INET6, &s->sin6_addr, peer_name, INET6_ADDRSTRLEN); + } + } else { + return NULL; + } + return peer_name; +} + +#ifdef __linux__ +static void +set_linger(int fd) +{ + struct linger so_linger; + memset(&so_linger, 0, sizeof(struct linger)); + so_linger.l_onoff = 1; + so_linger.l_linger = 0; + setsockopt(fd, SOL_SOCKET, SO_LINGER, &so_linger, sizeof so_linger); +} +#endif + +static void +reset_addr(int fd) +{ + char *peer_name; + peer_name = get_peer_name(fd); + if (peer_name != NULL) { + remove_from_block_list(peer_name); + } +} + +static void +report_addr(int fd, int err_level) +{ +#ifdef __linux__ + set_linger(fd); +#endif + + char *peer_name; + peer_name = get_peer_name(fd); + if (peer_name != NULL) { + LOGE("failed to handshake with %s", peer_name); + update_block_list(peer_name, err_level); + } +} + +int +setfastopen(int fd) +{ + int s = 0; +#ifdef TCP_FASTOPEN + if (fast_open) { +#ifdef __APPLE__ + int opt = 1; +#else + int opt = 5; +#endif + s = setsockopt(fd, IPPROTO_TCP, TCP_FASTOPEN, &opt, sizeof(opt)); + + if (s == -1) { + if (errno == EPROTONOSUPPORT || errno == ENOPROTOOPT) { + LOGE("fast open is not supported on this platform"); + fast_open = 0; + } else { + ERROR("setsockopt"); + } + } + } +#endif + return s; +} + +#ifndef __MINGW32__ +int +setnonblocking(int fd) +{ + int flags; + if (-1 == (flags = fcntl(fd, F_GETFL, 0))) { + flags = 0; + } + return fcntl(fd, F_SETFL, flags | O_NONBLOCK); +} + +#endif + +int +create_and_bind(const char *host, const char *port, int mptcp) +{ + struct addrinfo hints; + struct addrinfo *result, *rp, *ipv4v6bindall; + int s, listen_sock; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; /* Return IPv4 and IPv6 choices */ + hints.ai_socktype = SOCK_STREAM; /* We want a TCP socket */ + hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG; /* For wildcard IP address */ + hints.ai_protocol = IPPROTO_TCP; + + for (int i = 1; i < 8; i++) { + s = getaddrinfo(host, port, &hints, &result); + if (s == 0) { + break; + } else { + sleep(pow(2, i)); + LOGE("failed to resolve server name, wait %.0f seconds", pow(2, i)); + } + } + + if (s != 0) { + LOGE("getaddrinfo: %s", gai_strerror(s)); + return -1; + } + + rp = result; + + /* + * On Linux, with net.ipv6.bindv6only = 0 (the default), getaddrinfo(NULL) with + * AI_PASSIVE returns 0.0.0.0 and :: (in this order). AI_PASSIVE was meant to + * return a list of addresses to listen on, but it is impossible to listen on + * 0.0.0.0 and :: at the same time, if :: implies dualstack mode. + */ + if (!host) { + ipv4v6bindall = result; + + /* Loop over all address infos found until a IPV6 address is found. */ + while (ipv4v6bindall) { + if (ipv4v6bindall->ai_family == AF_INET6) { + rp = ipv4v6bindall; /* Take first IPV6 address available */ + break; + } + ipv4v6bindall = ipv4v6bindall->ai_next; /* Get next address info, if any */ + } + } + + for (/*rp = result*/; rp != NULL; rp = rp->ai_next) { + listen_sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if (listen_sock == -1) { + continue; + } + + if (rp->ai_family == AF_INET6) { + int ipv6only = host ? 1 : 0; + setsockopt(listen_sock, IPPROTO_IPV6, IPV6_V6ONLY, &ipv6only, sizeof(ipv6only)); + } + + int opt = 1; + setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); +#ifdef SO_NOSIGPIPE + setsockopt(listen_sock, SOL_SOCKET, SO_NOSIGPIPE, &opt, sizeof(opt)); +#endif + int err = set_reuseport(listen_sock); + if (err == 0) { + LOGI("tcp port reuse enabled"); + } + + if (mptcp == 1) { + int err = setsockopt(listen_sock, SOL_TCP, MPTCP_ENABLED, &opt, sizeof(opt)); + if (err == -1) { + ERROR("failed to enable multipath TCP"); + } + } + + s = bind(listen_sock, rp->ai_addr, rp->ai_addrlen); + if (s == 0) { + /* We managed to bind successfully! */ + break; + } else { + ERROR("bind"); + } + + close(listen_sock); + } + + if (rp == NULL) { + LOGE("Could not bind"); + return -1; + } + + freeaddrinfo(result); + + return listen_sock; +} + +static remote_t * +connect_to_remote(EV_P_ struct addrinfo *res, + server_t *server) +{ + int sockfd; +#ifdef SET_INTERFACE + const char *iface = server->listen_ctx->iface; +#endif + + if (acl) { + char ipstr[INET6_ADDRSTRLEN]; + memset(ipstr, 0, INET6_ADDRSTRLEN); + + if (res->ai_addr->sa_family == AF_INET) { + struct sockaddr_in *s = (struct sockaddr_in *)res->ai_addr; + dns_ntop(AF_INET, &s->sin_addr, ipstr, INET_ADDRSTRLEN); + } else if (res->ai_addr->sa_family == AF_INET6) { + struct sockaddr_in6 *s = (struct sockaddr_in6 *)res->ai_addr; + dns_ntop(AF_INET6, &s->sin6_addr, ipstr, INET6_ADDRSTRLEN); + } + + if (outbound_block_match_host(ipstr) == 1) { + if (verbose) + LOGI("outbound blocked %s", ipstr); + return NULL; + } + } + + // initialize remote socks + sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + if (sockfd == -1) { + ERROR("socket"); + close(sockfd); + return NULL; + } + + int opt = 1; + setsockopt(sockfd, SOL_TCP, TCP_NODELAY, &opt, sizeof(opt)); +#ifdef SO_NOSIGPIPE + setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, &opt, sizeof(opt)); +#endif + setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); + + // setup remote socks + + if (setnonblocking(sockfd) == -1) + ERROR("setnonblocking"); + + if (bind_address != NULL) + if (bind_to_address(sockfd, bind_address) == -1) { + ERROR("bind_to_address"); + close(sockfd); + return NULL; + } + +#ifdef SET_INTERFACE + if (iface) { + if (setinterface(sockfd, iface) == -1) { + ERROR("setinterface"); + close(sockfd); + return NULL; + } + } +#endif + + remote_t *remote = new_remote(sockfd); + +#ifdef TCP_FASTOPEN + if (fast_open) { +#ifdef __APPLE__ + ((struct sockaddr_in *)(res->ai_addr))->sin_len = sizeof(struct sockaddr_in); + sa_endpoints_t endpoints; + memset((char *)&endpoints, 0, sizeof(endpoints)); + endpoints.sae_dstaddr = res->ai_addr; + endpoints.sae_dstaddrlen = res->ai_addrlen; + + struct iovec iov; + iov.iov_base = server->buf->array + server->buf->idx; + iov.iov_len = server->buf->len; + size_t len; + int s = connectx(sockfd, &endpoints, SAE_ASSOCID_ANY, CONNECT_DATA_IDEMPOTENT, + &iov, 1, &len, NULL); + if (s == 0) { + s = len; + } +#else + ssize_t s = sendto(sockfd, server->buf->array + server->buf->idx, + server->buf->len, MSG_FASTOPEN, res->ai_addr, + res->ai_addrlen); +#endif + if (s == -1) { + if (errno == CONNECT_IN_PROGRESS || errno == EAGAIN + || errno == EWOULDBLOCK) { + // The remote server doesn't support tfo or it's the first connection to the server. + // It will automatically fall back to conventional TCP. + } else if (errno == EOPNOTSUPP || errno == EPROTONOSUPPORT || + errno == ENOPROTOOPT) { + // Disable fast open as it's not supported + fast_open = 0; + LOGE("fast open is not supported on this platform"); + } else { + ERROR("sendto"); + } + } else if (s <= server->buf->len) { + server->buf->idx += s; + server->buf->len -= s; + } else { + server->buf->idx = 0; + server->buf->len = 0; + } + } +#endif + + if (!fast_open) { + int r = connect(sockfd, res->ai_addr, res->ai_addrlen); + + if (r == -1 && errno != CONNECT_IN_PROGRESS) { + ERROR("connect"); + close_and_free_remote(EV_A_ remote); + return NULL; + } + } + + return remote; +} + +static void +server_recv_cb(EV_P_ ev_io *w, int revents) +{ + server_ctx_t *server_recv_ctx = (server_ctx_t *)w; + server_t *server = server_recv_ctx->server; + remote_t *remote = NULL; + + int len = server->buf->len; + buffer_t *buf = server->buf; + + if (server->stage > STAGE_PARSE) { + remote = server->remote; + buf = remote->buf; + len = 0; + + ev_timer_again(EV_A_ & server->recv_ctx->watcher); + } + + if (len > BUF_SIZE) { + ERROR("out of recv buffer"); + close_and_free_remote(EV_A_ remote); + close_and_free_server(EV_A_ server); + return; + } + + ssize_t r = recv(server->fd, buf->array + len, BUF_SIZE - len, 0); + + if (r == 0) { + // connection closed + if (verbose) { + LOGI("server_recv close the connection"); + } + close_and_free_remote(EV_A_ remote); + close_and_free_server(EV_A_ server); + return; + } else if (r == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + // no data + // continue to wait for recv + return; + } else { + ERROR("server recv"); + close_and_free_remote(EV_A_ remote); + close_and_free_server(EV_A_ server); + return; + } + } + + tx += r; + + if (server->stage == STAGE_ERROR) { + server->buf->len = 0; + server->buf->idx = 0; + return; + } + + // handle incomplete header part 1 + if (server->stage == STAGE_INIT) { + buf->len += r; + if (buf->len <= enc_get_iv_len() + 1) { + // wait for more + return; + } + } else { + buf->len = r; + } + + // SSR beg + + if (server->obfs_plugin) { + obfs_class *obfs_plugin = server->obfs_plugin; + if (obfs_plugin->server_decode) { + int needsendback = 0; + + if(obfs_compatible == 1) + { + char *back_buf = (char*)malloc(sizeof(buffer_t)); + memcpy(back_buf, buf, sizeof(buffer_t)); + buf->len = obfs_plugin->server_decode(server->obfs, &buf->array, buf->len, &buf->capacity, &needsendback); + + if ((int)buf->len < 0) + { + LOGE("obfs_compatible"); + memcpy(buf, back_buf, sizeof(buffer_t)); + free(back_buf); + server->obfs_compatible_state = 1; + } + } + else + { + buf->len = obfs_plugin->server_decode(server->obfs, &buf->array, buf->len, &buf->capacity, &needsendback); + if ((int)buf->len < 0) { + LOGE("server_decode"); + close_and_free_remote(EV_A_ remote); + close_and_free_server(EV_A_ server); + return; + } + } + + if (needsendback) { + size_t capacity = BUF_SIZE; + char *sendback_buf = (char*)malloc(capacity); + obfs_class *obfs_plugin = server->obfs_plugin; + if (obfs_plugin->server_encode) { + int len = obfs_plugin->server_encode(server->obfs, &sendback_buf, 0, &capacity); + send(server->fd, sendback_buf, len, 0); + } + free(sendback_buf); + return; + } + } + } + + int err = ss_decrypt(buf, server->d_ctx, BUF_SIZE); + + if (err) { + report_addr(server->fd, MALICIOUS); + close_and_free_remote(EV_A_ remote); + close_and_free_server(EV_A_ server); + return; + } + + if (server->protocol_plugin) { + obfs_class *protocol_plugin = server->protocol_plugin; + if (protocol_plugin->server_post_decrypt) { + + if(protocol_compatible == 1) + { + char *back_buf = (char*)malloc(sizeof(buffer_t)); + memcpy(back_buf, buf, sizeof(buffer_t)); + buf->len = protocol_plugin->server_post_decrypt(server->protocol, &buf->array, buf->len, &buf->capacity); + + if ((int)buf->len < 0) { + LOGE("protocol_compatible"); + memcpy(buf, back_buf, sizeof(buffer_t)); + free(back_buf); + server->protocol_compatible_state = 1; + } + if ( buf->len == 0 ) + { + LOGE("protocol_compatible"); + memcpy(buf, back_buf, sizeof(buffer_t)); + free(back_buf); + server->protocol_compatible_state = 1; + } + } + else + { + buf->len = protocol_plugin->server_post_decrypt(server->protocol, &buf->array, buf->len, &buf->capacity); + if ((int)buf->len < 0) { + LOGE("server_post_decrypt"); + close_and_free_remote(EV_A_ remote); + close_and_free_server(EV_A_ server); + return; + } + if ( buf->len == 0 ) + { + LOGE("server_post_decrypt"); + return; + } + } + } + } + // SSR end + + // handle incomplete header part 2 + if (server->stage == STAGE_INIT) { + int ret = is_header_complete(server->buf); + if (ret == 1) { + bfree(server->header_buf); + ss_free(server->header_buf); + server->stage = STAGE_PARSE; + } else if (ret == -1) { + server->stage = STAGE_ERROR; + report_addr(server->fd, MALFORMED); + server->buf->len = 0; + server->buf->idx = 0; + return; + } else { + server->stage = STAGE_HANDSHAKE; + } + } + + if (server->stage == STAGE_HANDSHAKE) { + size_t header_len = server->header_buf->len; + brealloc(server->header_buf, server->buf->len + header_len, BUF_SIZE); + memcpy(server->header_buf->array + header_len, + server->buf->array, server->buf->len); + server->header_buf->len = server->buf->len + header_len; + + int ret = is_header_complete(server->buf); + + if (ret == 1) { + brealloc(server->buf, server->header_buf->len, BUF_SIZE); + memcpy(server->buf->array, server->header_buf->array, server->header_buf->len); + server->buf->len = server->header_buf->len; + bfree(server->header_buf); + ss_free(server->header_buf); + server->stage = STAGE_PARSE; + } else { + if (ret == -1) + server->stage = STAGE_ERROR; + server->buf->len = 0; + server->buf->idx = 0; + return; + } + } + + // handshake and transmit data + if (server->stage == STAGE_STREAM) { + if (server->auth && !ss_check_hash(remote->buf, server->chunk, server->d_ctx, BUF_SIZE)) { + LOGE("hash error"); + report_addr(server->fd, BAD); + close_and_free_server(EV_A_ server); + close_and_free_remote(EV_A_ remote); + return; + } + + int s = send(remote->fd, remote->buf->array, remote->buf->len, 0); + if (s == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + // no data, wait for send + remote->buf->idx = 0; + ev_io_stop(EV_A_ & server_recv_ctx->io); + ev_io_start(EV_A_ & remote->send_ctx->io); + } else { + ERROR("server_recv_send"); + close_and_free_remote(EV_A_ remote); + close_and_free_server(EV_A_ server); + } + } else if (s < remote->buf->len) { + remote->buf->len -= s; + remote->buf->idx = s; + ev_io_stop(EV_A_ & server_recv_ctx->io); + ev_io_start(EV_A_ & remote->send_ctx->io); + } + return; + } else if (server->stage == STAGE_PARSE) { + /* + * Shadowsocks TCP Relay Header: + * + * +------+----------+----------+----------------+ + * | ATYP | DST.ADDR | DST.PORT | HMAC-SHA1 | + * +------+----------+----------+----------------+ + * | 1 | Variable | 2 | 10 | + * +------+----------+----------+----------------+ + * + * If ATYP & ONETIMEAUTH_FLAG(0x10) != 0, Authentication (HMAC-SHA1) is enabled. + * + * The key of HMAC-SHA1 is (IV + KEY) and the input is the whole header. + * The output of HMAC-SHA is truncated to 10 bytes (leftmost bits). + */ + + /* + * Shadowsocks Request's Chunk Authentication for TCP Relay's payload + * (No chunk authentication for response's payload): + * + * +------+-----------+-------------+------+ + * | LEN | HMAC-SHA1 | DATA | ... + * +------+-----------+-------------+------+ + * | 2 | 10 | Variable | ... + * +------+-----------+-------------+------+ + * + * The key of HMAC-SHA1 is (IV + CHUNK ID) + * The output of HMAC-SHA is truncated to 10 bytes (leftmost bits). + */ + + int offset = 0; + int need_query = 0; + char atyp = server->buf->array[offset++]; + char host[257] = { 0 }; + uint16_t port = 0; + struct addrinfo info; + struct sockaddr_storage storage; + memset(&info, 0, sizeof(struct addrinfo)); + memset(&storage, 0, sizeof(struct sockaddr_storage)); + + if (auth || (atyp & ONETIMEAUTH_FLAG)) { + size_t header_len = parse_header_len(atyp, server->buf->array, offset); + size_t len = server->buf->len; + + if (header_len == 0 || len < offset + header_len + ONETIMEAUTH_BYTES) { + report_addr(server->fd, MALFORMED); + close_and_free_server(EV_A_ server); + return; + } + + server->buf->len = offset + header_len + ONETIMEAUTH_BYTES; + if (ss_onetimeauth_verify(server->buf, server->d_ctx->evp.iv)) { + report_addr(server->fd, BAD); + close_and_free_server(EV_A_ server); + return; + } + + server->buf->len = len; + server->auth = 1; + } + + // get remote addr and port + if ((atyp & ADDRTYPE_MASK) == 1) { + // IP V4 + struct sockaddr_in *addr = (struct sockaddr_in *)&storage; + size_t in_addr_len = sizeof(struct in_addr); + addr->sin_family = AF_INET; + if (server->buf->len >= in_addr_len + 3) { + addr->sin_addr = *(struct in_addr *)(server->buf->array + offset); + dns_ntop(AF_INET, (const void *)(server->buf->array + offset), + host, INET_ADDRSTRLEN); + offset += in_addr_len; + } else { + LOGE("invalid header with addr type %d", atyp); + report_addr(server->fd, MALFORMED); + close_and_free_server(EV_A_ server); + return; + } + addr->sin_port = *(uint16_t *)(server->buf->array + offset); + info.ai_family = AF_INET; + info.ai_socktype = SOCK_STREAM; + info.ai_protocol = IPPROTO_TCP; + info.ai_addrlen = sizeof(struct sockaddr_in); + info.ai_addr = (struct sockaddr *)addr; + } else if ((atyp & ADDRTYPE_MASK) == 3) { + // Domain name + uint8_t name_len = *(uint8_t *)(server->buf->array + offset); + if (name_len + 4 <= server->buf->len) { + memcpy(host, server->buf->array + offset + 1, name_len); + offset += name_len + 1; + } else { + LOGE("invalid name length: %d", name_len); + report_addr(server->fd, MALFORMED); + close_and_free_server(EV_A_ server); + return; + } + if (acl && outbound_block_match_host(host) == 1) { + if (verbose) + LOGI("outbound blocked %s", host); + close_and_free_server(EV_A_ server); + return; + } + struct cork_ip ip; + if (cork_ip_init(&ip, host) != -1) { + info.ai_socktype = SOCK_STREAM; + info.ai_protocol = IPPROTO_TCP; + if (ip.version == 4) { + struct sockaddr_in *addr = (struct sockaddr_in *)&storage; + dns_pton(AF_INET, host, &(addr->sin_addr)); + addr->sin_port = *(uint16_t *)(server->buf->array + offset); + addr->sin_family = AF_INET; + info.ai_family = AF_INET; + info.ai_addrlen = sizeof(struct sockaddr_in); + info.ai_addr = (struct sockaddr *)addr; + } else if (ip.version == 6) { + struct sockaddr_in6 *addr = (struct sockaddr_in6 *)&storage; + dns_pton(AF_INET6, host, &(addr->sin6_addr)); + addr->sin6_port = *(uint16_t *)(server->buf->array + offset); + addr->sin6_family = AF_INET6; + info.ai_family = AF_INET6; + info.ai_addrlen = sizeof(struct sockaddr_in6); + info.ai_addr = (struct sockaddr *)addr; + } + } else { + if (!validate_hostname(host, name_len)) { + LOGE("invalid host name"); + report_addr(server->fd, MALFORMED); + close_and_free_server(EV_A_ server); + return; + } + need_query = 1; + } + } else if ((atyp & ADDRTYPE_MASK) == 4) { + // IP V6 + struct sockaddr_in6 *addr = (struct sockaddr_in6 *)&storage; + size_t in6_addr_len = sizeof(struct in6_addr); + addr->sin6_family = AF_INET6; + if (server->buf->len >= in6_addr_len + 3) { + addr->sin6_addr = *(struct in6_addr *)(server->buf->array + offset); + dns_ntop(AF_INET6, (const void *)(server->buf->array + offset), + host, INET6_ADDRSTRLEN); + offset += in6_addr_len; + } else { + LOGE("invalid header with addr type %d", atyp); + report_addr(server->fd, MALFORMED); + close_and_free_server(EV_A_ server); + return; + } + addr->sin6_port = *(uint16_t *)(server->buf->array + offset); + info.ai_family = AF_INET6; + info.ai_socktype = SOCK_STREAM; + info.ai_protocol = IPPROTO_TCP; + info.ai_addrlen = sizeof(struct sockaddr_in6); + info.ai_addr = (struct sockaddr *)addr; + } + + if (offset == 1) { + LOGE("invalid header with addr type %d", atyp); + report_addr(server->fd, MALFORMED); + close_and_free_server(EV_A_ server); + return; + } + + port = (*(uint16_t *)(server->buf->array + offset)); + + offset += 2; + + if (server->auth) { + offset += ONETIMEAUTH_BYTES; + } + + if (server->buf->len < offset) { + report_addr(server->fd, MALFORMED); + close_and_free_server(EV_A_ server); + return; + } else { + server->buf->len -= offset; + memmove(server->buf->array, server->buf->array + offset, server->buf->len); + } + + if (verbose) { + if ((atyp & ADDRTYPE_MASK) == 4) + LOGI("connect to [%s]:%d", host, ntohs(port)); + else + LOGI("connect to %s:%d", host, ntohs(port)); + } + + if (server->auth && !ss_check_hash(server->buf, server->chunk, server->d_ctx, BUF_SIZE)) { + LOGE("hash error"); + report_addr(server->fd, BAD); + close_and_free_server(EV_A_ server); + return; + } + + + if (!need_query) { + remote_t *remote = connect_to_remote(EV_A_ &info, server); + + if (remote == NULL) { + LOGE("connect error"); + close_and_free_server(EV_A_ server); + return; + } else { + server->remote = remote; + remote->server = server; + + // XXX: should handle buffer carefully + if (server->buf->len > 0) { + memcpy(remote->buf->array, server->buf->array, server->buf->len); + remote->buf->len = server->buf->len; + remote->buf->idx = 0; + server->buf->len = 0; + server->buf->idx = 0; + } + + // waiting on remote connected event + ev_io_stop(EV_A_ & server_recv_ctx->io); + ev_io_start(EV_A_ & remote->send_ctx->io); + } + } else { + query_t *query = (query_t *)ss_malloc(sizeof(query_t)); + query->server = server; + snprintf(query->hostname, 256, "%s", host); + + server->stage = STAGE_RESOLVE; + server->query = resolv_query(host, server_resolve_cb, + query_free_cb, query, port); + + ev_io_stop(EV_A_ & server_recv_ctx->io); + } + + return; + } + // should not reach here + FATAL("server context error"); +} + +static void +server_send_cb(EV_P_ ev_io *w, int revents) +{ + server_ctx_t *server_send_ctx = (server_ctx_t *)w; + server_t *server = server_send_ctx->server; + remote_t *remote = server->remote; + + if (remote == NULL) { + LOGE("invalid server"); + close_and_free_server(EV_A_ server); + return; + } + + if (server->buf->len == 0) { + // close and free + if (verbose) { + LOGI("server_send close the connection"); + } + close_and_free_remote(EV_A_ remote); + close_and_free_server(EV_A_ server); + return; + } else { + // has data to send + ssize_t s = send(server->fd, server->buf->array + server->buf->idx, + server->buf->len, 0); + if (s == -1) { + if (errno != EAGAIN && errno != EWOULDBLOCK) { + ERROR("server_send_send"); + close_and_free_remote(EV_A_ remote); + close_and_free_server(EV_A_ server); + } + return; + } else if (s < server->buf->len) { + // partly sent, move memory, wait for the next time to send + server->buf->len -= s; + server->buf->idx += s; + return; + } else { + // all sent out, wait for reading + server->buf->len = 0; + server->buf->idx = 0; + ev_io_stop(EV_A_ & server_send_ctx->io); + if (remote != NULL) { + ev_io_start(EV_A_ & remote->recv_ctx->io); + return; + } else { + LOGE("invalid remote"); + close_and_free_remote(EV_A_ remote); + close_and_free_server(EV_A_ server); + return; + } + } + } +} + +static void +block_list_clear_cb(EV_P_ ev_timer *watcher, int revents) +{ + clear_block_list(); +} + +static void +server_timeout_cb(EV_P_ ev_timer *watcher, int revents) +{ + server_ctx_t *server_ctx + = cork_container_of(watcher, server_ctx_t, watcher); + server_t *server = server_ctx->server; + remote_t *remote = server->remote; + + if (verbose) { + LOGI("TCP connection timeout"); + } + + if (server->stage < STAGE_PARSE) { + if (verbose) { + size_t len = server->stage ? + server->header_buf->len : server->buf->len; +#ifdef __MINGW32__ + LOGI("incomplete header: %u", len); +#else + LOGI("incomplete header: %zu", len); +#endif + } + report_addr(server->fd, SUSPICIOUS); + } + + close_and_free_remote(EV_A_ remote); + close_and_free_server(EV_A_ server); +} + +static void +query_free_cb(void *data) +{ + if (data != NULL) { + ss_free(data); + } +} + +static void +server_resolve_cb(struct sockaddr *addr, void *data) +{ + query_t *query = (query_t *)data; + server_t *server = query->server; + struct ev_loop *loop = server->listen_ctx->loop; + + server->query = NULL; + + if (addr == NULL) { + LOGE("unable to resolve %s", query->hostname); + close_and_free_server(EV_A_ server); + } else { + if (verbose) { + LOGI("successfully resolved %s", query->hostname); + } + + struct addrinfo info; + memset(&info, 0, sizeof(struct addrinfo)); + info.ai_socktype = SOCK_STREAM; + info.ai_protocol = IPPROTO_TCP; + info.ai_addr = addr; + + if (addr->sa_family == AF_INET) { + info.ai_family = AF_INET; + info.ai_addrlen = sizeof(struct sockaddr_in); + } else if (addr->sa_family == AF_INET6) { + info.ai_family = AF_INET6; + info.ai_addrlen = sizeof(struct sockaddr_in6); + } + + remote_t *remote = connect_to_remote(EV_A_ &info, server); + + if (remote == NULL) { + close_and_free_server(EV_A_ server); + } else { + server->remote = remote; + remote->server = server; + + // XXX: should handle buffer carefully + if (server->buf->len > 0) { + memcpy(remote->buf->array, server->buf->array + server->buf->idx, + server->buf->len); + remote->buf->len = server->buf->len; + remote->buf->idx = 0; + server->buf->len = 0; + server->buf->idx = 0; + } + + // listen to remote connected event + ev_io_start(EV_A_ & remote->send_ctx->io); + } + } +} + +static void +remote_recv_cb(EV_P_ ev_io *w, int revents) +{ + remote_ctx_t *remote_recv_ctx = (remote_ctx_t *)w; + remote_t *remote = remote_recv_ctx->remote; + server_t *server = remote->server; + + if (server == NULL) { + LOGE("invalid server"); + close_and_free_remote(EV_A_ remote); + return; + } + + ev_timer_again(EV_A_ & server->recv_ctx->watcher); + + ssize_t r = recv(remote->fd, server->buf->array, BUF_SIZE, 0); + + if (r == 0) { + // connection closed + if (verbose) { + LOGI("remote_recv close the connection"); + } + close_and_free_remote(EV_A_ remote); + close_and_free_server(EV_A_ server); + return; + } else if (r == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + // no data + // continue to wait for recv + return; + } else { + ERROR("remote recv"); + close_and_free_remote(EV_A_ remote); + close_and_free_server(EV_A_ server); + return; + } + } + + rx += r; + + server->buf->len = r; + + // SSR beg + server_info _server_info; + if (server->obfs_plugin) { + server->obfs_plugin->get_server_info(server->obfs, &_server_info); + _server_info.head_len = get_head_size(server->buf->array, server->buf->len, 30); + server->obfs_plugin->set_server_info(server->obfs, &_server_info); + } + + if (server->protocol_plugin && server->obfs_compatible_state == 0) { + obfs_class *protocol_plugin = server->protocol_plugin; + if (protocol_plugin->server_pre_encrypt) { + server->buf->len = protocol_plugin->server_pre_encrypt(server->protocol, &server->buf->array, server->buf->len, &server->buf->capacity); + } + } + + int err = ss_encrypt(server->buf, server->e_ctx, BUF_SIZE); + + if (err) { + LOGE("invalid password or cipher"); + close_and_free_remote(EV_A_ remote); + close_and_free_server(EV_A_ server); + return; + } + + if (server->obfs_plugin && server->obfs_compatible_state == 0) { + obfs_class *obfs_plugin = server->obfs_plugin; + if (obfs_plugin->server_encode) { + server->buf->len = obfs_plugin->server_encode(server->obfs, &server->buf->array, server->buf->len, &server->buf->capacity); + } + } + // SSR end + + int s = send(server->fd, server->buf->array, server->buf->len, 0); + + if (s == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + // no data, wait for send + server->buf->idx = 0; + ev_io_stop(EV_A_ & remote_recv_ctx->io); + ev_io_start(EV_A_ & server->send_ctx->io); + } else { + ERROR("remote_recv_send"); + close_and_free_remote(EV_A_ remote); + close_and_free_server(EV_A_ server); + return; + } + } else if (s < server->buf->len) { + server->buf->len -= s; + server->buf->idx = s; + ev_io_stop(EV_A_ & remote_recv_ctx->io); + ev_io_start(EV_A_ & server->send_ctx->io); + } + + // Disable TCP_NODELAY after the first response are sent + if (!remote->recv_ctx->connected) { + int opt = 0; + setsockopt(server->fd, SOL_TCP, TCP_NODELAY, &opt, sizeof(opt)); + setsockopt(remote->fd, SOL_TCP, TCP_NODELAY, &opt, sizeof(opt)); + remote->recv_ctx->connected = 1; + } +} + +static void +remote_send_cb(EV_P_ ev_io *w, int revents) +{ + remote_ctx_t *remote_send_ctx = (remote_ctx_t *)w; + remote_t *remote = remote_send_ctx->remote; + server_t *server = remote->server; + + if (server == NULL) { + LOGE("invalid server"); + close_and_free_remote(EV_A_ remote); + return; + } + + if (!remote_send_ctx->connected) { + struct sockaddr_storage addr; + socklen_t len = sizeof(struct sockaddr_storage); + memset(&addr, 0, len); + int r = getpeername(remote->fd, (struct sockaddr *)&addr, &len); + if (r == 0) { + if (verbose) { + LOGI("remote connected"); + } + remote_send_ctx->connected = 1; + + // Clear the state of this address in the block list + reset_addr(server->fd); + + if (remote->buf->len == 0) { + server->stage = STAGE_STREAM; + ev_io_stop(EV_A_ & remote_send_ctx->io); + ev_io_start(EV_A_ & server->recv_ctx->io); + ev_io_start(EV_A_ & remote->recv_ctx->io); + return; + } + } else { + ERROR("getpeername"); + // not connected + close_and_free_remote(EV_A_ remote); + close_and_free_server(EV_A_ server); + return; + } + } + + if (remote->buf->len == 0) { + // close and free + if (verbose) { + LOGI("remote_send close the connection"); + } + close_and_free_remote(EV_A_ remote); + close_and_free_server(EV_A_ server); + return; + } else { + // has data to send + ssize_t s = send(remote->fd, remote->buf->array + remote->buf->idx, + remote->buf->len, 0); + if (s == -1) { + if (errno != EAGAIN && errno != EWOULDBLOCK) { + ERROR("remote_send_send"); + // close and free + close_and_free_remote(EV_A_ remote); + close_and_free_server(EV_A_ server); + } + return; + } else if (s < remote->buf->len) { + // partly sent, move memory, wait for the next time to send + remote->buf->len -= s; + remote->buf->idx += s; + return; + } else { + // all sent out, wait for reading + remote->buf->len = 0; + remote->buf->idx = 0; + ev_io_stop(EV_A_ & remote_send_ctx->io); + if (server != NULL) { + ev_io_start(EV_A_ & server->recv_ctx->io); + if (server->stage != STAGE_STREAM) { + server->stage = STAGE_STREAM; + ev_io_start(EV_A_ & remote->recv_ctx->io); + } + } else { + LOGE("invalid server"); + close_and_free_remote(EV_A_ remote); + close_and_free_server(EV_A_ server); + } + return; + } + } +} + +static remote_t * +new_remote(int fd) +{ + if (verbose) { + remote_conn++; + } + + remote_t *remote; + + remote = ss_malloc(sizeof(remote_t)); + remote->recv_ctx = ss_malloc(sizeof(remote_ctx_t)); + remote->send_ctx = ss_malloc(sizeof(remote_ctx_t)); + remote->buf = ss_malloc(sizeof(buffer_t)); + remote->fd = fd; + remote->recv_ctx->remote = remote; + remote->recv_ctx->connected = 0; + remote->send_ctx->remote = remote; + remote->send_ctx->connected = 0; + remote->server = NULL; + + ev_io_init(&remote->recv_ctx->io, remote_recv_cb, fd, EV_READ); + ev_io_init(&remote->send_ctx->io, remote_send_cb, fd, EV_WRITE); + + balloc(remote->buf, BUF_SIZE); + + return remote; +} + +static void +free_remote(remote_t *remote) +{ + if (remote->server != NULL) { + remote->server->remote = NULL; + } + if (remote->buf != NULL) { + bfree(remote->buf); + ss_free(remote->buf); + } + ss_free(remote->recv_ctx); + ss_free(remote->send_ctx); + ss_free(remote); +} + +static void +close_and_free_remote(EV_P_ remote_t *remote) +{ + if (remote != NULL) { + ev_io_stop(EV_A_ & remote->send_ctx->io); + ev_io_stop(EV_A_ & remote->recv_ctx->io); + close(remote->fd); + free_remote(remote); + if (verbose) { + remote_conn--; + LOGI("current remote connection: %d", remote_conn); + } + } +} + +static server_t * +new_server(int fd, listen_ctx_t *listener) +{ + if (verbose) { + server_conn++; + } + + server_t *server; + server = ss_malloc(sizeof(server_t)); + + memset(server, 0, sizeof(server_t)); + + server->recv_ctx = ss_malloc(sizeof(server_ctx_t)); + server->send_ctx = ss_malloc(sizeof(server_ctx_t)); + server->buf = ss_malloc(sizeof(buffer_t)); + server->header_buf = ss_malloc(sizeof(buffer_t)); + server->fd = fd; + server->recv_ctx->server = server; + server->recv_ctx->connected = 0; + server->send_ctx->server = server; + server->send_ctx->connected = 0; + server->stage = STAGE_INIT; + server->query = NULL; + server->listen_ctx = listener; + server->remote = NULL; + + if (listener->method) { + server->e_ctx = ss_malloc(sizeof(enc_ctx_t)); + server->d_ctx = ss_malloc(sizeof(enc_ctx_t)); + enc_ctx_init(listener->method, server->e_ctx, 1); + enc_ctx_init(listener->method, server->d_ctx, 0); + } else { + server->e_ctx = NULL; + server->d_ctx = NULL; + } + + int request_timeout = min(MAX_REQUEST_TIMEOUT, listener->timeout) + + rand() % MAX_REQUEST_TIMEOUT; + + ev_io_init(&server->recv_ctx->io, server_recv_cb, fd, EV_READ); + ev_io_init(&server->send_ctx->io, server_send_cb, fd, EV_WRITE); + ev_timer_init(&server->recv_ctx->watcher, server_timeout_cb, + request_timeout, listener->timeout); + + balloc(server->buf, BUF_SIZE); + balloc(server->header_buf, BUF_SIZE); + + server->chunk = (chunk_t *)malloc(sizeof(chunk_t)); + memset(server->chunk, 0, sizeof(chunk_t)); + server->chunk->buf = ss_malloc(sizeof(buffer_t)); + memset(server->chunk->buf, 0, sizeof(buffer_t)); + + cork_dllist_add(&connections, &server->entries); + + return server; +} + +static void +free_server(server_t *server) +{ + cork_dllist_remove(&server->entries); + + if (server->chunk != NULL) { + if (server->chunk->buf != NULL) { + bfree(server->chunk->buf); + ss_free(server->chunk->buf); + } + ss_free(server->chunk); + } + if (server->remote != NULL) { + server->remote->server = NULL; + } + if (server->e_ctx != NULL) { + cipher_context_release(&server->e_ctx->evp); + ss_free(server->e_ctx); + } + if (server->d_ctx != NULL) { + cipher_context_release(&server->d_ctx->evp); + ss_free(server->d_ctx); + } + if (server->buf != NULL) { + bfree(server->buf); + ss_free(server->buf); + } + if (server->header_buf != NULL) { + bfree(server->header_buf); + ss_free(server->header_buf); + } + + ss_free(server->recv_ctx); + ss_free(server->send_ctx); + ss_free(server); +} + +static void +close_and_free_server(EV_P_ server_t *server) +{ + if (server != NULL) { + if (server->query != NULL) { + resolv_cancel(server->query); + server->query = NULL; + } + ev_io_stop(EV_A_ & server->send_ctx->io); + ev_io_stop(EV_A_ & server->recv_ctx->io); + ev_timer_stop(EV_A_ & server->recv_ctx->watcher); + close(server->fd); + free_server(server); + if (verbose) { + server_conn--; + LOGI("current server connection: %d", server_conn); + } + } +} + +static void +signal_cb(EV_P_ ev_signal *w, int revents) +{ + if (revents & EV_SIGNAL) { + switch (w->signum) { + case SIGINT: + case SIGTERM: + ev_unloop(EV_A_ EVUNLOOP_ALL); + } + } +} + +static void +accept_cb(EV_P_ ev_io *w, int revents) +{ + listen_ctx_t *listener = (listen_ctx_t *)w; + int serverfd = accept(listener->fd, NULL, NULL); + if (serverfd == -1) { + ERROR("accept"); + return; + } + + char *peer_name = get_peer_name(serverfd); + if (peer_name != NULL) { + int in_white_list = 0; + if (acl) { + if ((get_acl_mode() == BLACK_LIST && acl_match_host(peer_name) == 1) + || (get_acl_mode() == WHITE_LIST && acl_match_host(peer_name) >= 0)) { + LOGE("Access denied from %s", peer_name); + close(serverfd); + return; + } else if (acl_match_host(peer_name) == -1) { + in_white_list = 1; + } + } + if (!in_white_list && check_block_list(peer_name)) { + LOGE("block all requests from %s", peer_name); +#ifdef __linux__ + set_linger(serverfd); +#endif + close(serverfd); + return; + } + } + + int opt = 1; + setsockopt(serverfd, SOL_TCP, TCP_NODELAY, &opt, sizeof(opt)); +#ifdef SO_NOSIGPIPE + setsockopt(serverfd, SOL_SOCKET, SO_NOSIGPIPE, &opt, sizeof(opt)); +#endif + setnonblocking(serverfd); + + if (verbose) { + LOGI("accept a connection"); + } + + server_t *server = new_server(serverfd, listener); + + // SSR beg + server->obfs_plugin = new_obfs_class(server->listen_ctx->obfs_name); + if (server->obfs_plugin) { + server->obfs = server->obfs_plugin->new_obfs(); + server->obfs_compatible_state = 0; + } + server->protocol_plugin = new_obfs_class(server->listen_ctx->protocol_name); + if (server->protocol_plugin) { + server->protocol = server->protocol_plugin->new_obfs(); + server->protocol_compatible_state = 0; + } + server_info _server_info; + memset(&_server_info, 0, sizeof(server_info)); + _server_info.param = server->listen_ctx->obfs_param; + if(server->obfs_plugin) + _server_info.g_data = server->obfs_plugin->init_data(); + _server_info.head_len = 7; + _server_info.iv = server->e_ctx->evp.iv; + _server_info.iv_len = enc_get_iv_len(); + _server_info.key = enc_get_key(); + _server_info.key_len = enc_get_key_len(); + _server_info.tcp_mss = 1460; + + if (server->obfs_plugin) + server->obfs_plugin->set_server_info(server->obfs, &_server_info); + + _server_info.param = server->listen_ctx->protocol_param; + if (server->protocol_plugin) + _server_info.g_data = server->protocol_plugin->init_data(); + + if (server->protocol_plugin) + server->protocol_plugin->set_server_info(server->protocol, &_server_info); + // SSR end + + ev_io_start(EV_A_ & server->recv_ctx->io); + ev_timer_start(EV_A_ & server->recv_ctx->watcher); +} + +int +main(int argc, char **argv) +{ + int i, c; + int pid_flags = 0; + int mptcp = 0; + int firewall = 0; + int mtu = 0; + char *user = NULL; + char *password = NULL; + char *timeout = NULL; + char *protocol = NULL; // SSR + char *protocol_param = NULL; // SSR + char *method = NULL; + char *obfs = NULL; // SSR + char *obfs_param = NULL; // SSR + char *pid_path = NULL; + char *conf_path = NULL; + char *iface = NULL; + + int server_num = 0; + const char *server_host[MAX_REMOTE_NUM]; + + char *nameservers[MAX_DNS_NUM + 1]; + int nameserver_num = 0; + + int option_index = 0; + static struct option long_options[] = { + { "fast-open", no_argument, 0, 0 }, + { "acl", required_argument, 0, 0 }, + { "manager-address", required_argument, 0, 0 }, + { "mtu", required_argument, 0, 0 }, + { "help", no_argument, 0, 0 }, +#ifdef __linux__ + { "mptcp", no_argument, 0, 0 }, + { "firewall", no_argument, 0, 0 }, +#endif + { 0, 0, 0, 0 } + }; + + opterr = 0; + + USE_TTY(); + + while ((c = getopt_long(argc, argv, "f:s:p:l:k:t:m:b:c:i:d:a:n:O:o:G:g:huUvA6", + long_options, &option_index)) != -1) { + switch (c) { + case 0: + if (option_index == 0) { + fast_open = 1; + } else if (option_index == 1) { + LOGI("initializing acl..."); + acl = !init_acl(optarg); + } else if (option_index == 2) { + manager_address = optarg; + } else if (option_index == 3) { + mtu = atoi(optarg); + LOGI("set MTU to %d", mtu); + } else if (option_index == 4) { + usage(); + exit(EXIT_SUCCESS); + } else if (option_index == 5) { + mptcp = 1; + LOGI("enable multipath TCP"); + } else if (option_index == 6) { + firewall = 1; + LOGI("enable firewall rules"); + } + break; + case 's': + if (server_num < MAX_REMOTE_NUM) { + server_host[server_num++] = optarg; + } + break; + case 'b': + bind_address = optarg; + break; + case 'p': + server_port = optarg; + break; + case 'k': + password = optarg; + break; + case 'f': + pid_flags = 1; + pid_path = optarg; + break; + case 't': + timeout = optarg; + break; + // SSR beg + case 'O': + protocol = optarg; + break; + case 'm': + method = optarg; + break; + case 'o': + obfs = optarg; + break; + case 'G': + protocol_param = optarg; + break; + case 'g': + obfs_param = optarg; + break; + // SSR end + case 'c': + conf_path = optarg; + break; + case 'i': + iface = optarg; + break; + case 'd': + if (nameserver_num < MAX_DNS_NUM) { + nameservers[nameserver_num++] = optarg; + } + break; + case 'a': + user = optarg; + break; +#ifdef HAVE_SETRLIMIT + case 'n': + nofile = atoi(optarg); + break; +#endif + case 'u': + mode = TCP_AND_UDP; + break; + case 'U': + mode = UDP_ONLY; + break; + case 'v': + verbose = 1; + break; + case 'h': + usage(); + exit(EXIT_SUCCESS); + case 'A': + auth = 1; + break; + case '6': + ipv6first = 1; + break; + case '?': + // The option character is not recognized. + LOGE("Unrecognized option: %s", optarg); + opterr = 1; + break; + } + } + + if (opterr) { + usage(); + exit(EXIT_FAILURE); + } + + if (argc == 1) { + if (conf_path == NULL) { + conf_path = DEFAULT_CONF_PATH; + } + } + + if (conf_path != NULL) { + jconf_t *conf = read_jconf(conf_path); + if (server_num == 0) { + server_num = conf->remote_num; + for (i = 0; i < server_num; i++) + server_host[i] = conf->remote_addr[i].host; + } + if (server_port == NULL) { + server_port = conf->remote_port; + } + if (password == NULL) { + password = conf->password; + } + // SSR beg + if (protocol == NULL) { + protocol = conf->protocol; + LOGI("protocol %s", protocol); + } + if (protocol_param == NULL) { + protocol_param = conf->protocol_param; + LOGI("protocol_param %s", obfs_param); + } + if (method == NULL) { + method = conf->method; + LOGI("method %s", method); + } + if (obfs == NULL) { + obfs = conf->obfs; + LOGI("obfs %s", obfs); + } + if (obfs_param == NULL) { + obfs_param = conf->obfs_param; + LOGI("obfs_param %s", obfs_param); + } + // SSR end + if (timeout == NULL) { + timeout = conf->timeout; + } + if (user == NULL) { + user = conf->user; + } + if (auth == 0) { + auth = conf->auth; + } + if (mode == TCP_ONLY) { + mode = conf->mode; + } + if (mtu == 0) { + mtu = conf->mtu; + } + if (mptcp == 0) { + mptcp = conf->mptcp; + } +#ifdef TCP_FASTOPEN + if (fast_open == 0) { + fast_open = conf->fast_open; + } +#endif +#ifdef HAVE_SETRLIMIT + if (nofile == 0) { + nofile = conf->nofile; + } +#endif + if (conf->nameserver != NULL) { + nameservers[nameserver_num++] = conf->nameserver; + } + if (ipv6first == 0) { + ipv6first = conf->ipv6_first; + } + } + + //_compatible + if(strlen(protocol)>11) + { + char *text; + text = (char*)malloc(12); + memcpy(text, protocol + strlen(protocol) - 11, 12); + + if(strcmp(text, "_compatible") == 0) + { + free(text); + text = (char*)malloc(strlen(protocol) - 11); + memcpy(text, protocol, strlen(protocol) - 11); + int length = strlen(protocol) - 11; + free(protocol); + obfs = (char*)malloc(length); + memset(protocol, 0x00, length); + memcpy(protocol, text, length); + LOGI("protocol compatible enable, %s", protocol); + free(text); + protocol_compatible = 1; + } + } + + if(strlen(obfs)>11) + { + char *text; + text = (char*)malloc(12); + memcpy(text, obfs + strlen(obfs) - 11, 12); + + if(strcmp(text, "_compatible") == 0) + { + free(text); + text = (char*)malloc(strlen(obfs) - 11); + memcpy(text, obfs, strlen(obfs) - 11); + int length = strlen(obfs) - 11; + free(obfs); + obfs = (char*)malloc(length); + memset(obfs, 0x00, length); + memcpy(obfs, text, length); + LOGI("obfs compatible enable, %s", obfs); + free(text); + obfs_compatible = 1; + } + } + + + if (server_num == 0) { + server_host[server_num++] = NULL; + } + + if (server_num == 0 || server_port == NULL || password == NULL) { + usage(); + exit(EXIT_FAILURE); + } + + if (protocol && strcmp(protocol, "verify_sha1") == 0) { + auth = 1; + protocol = NULL; + } + + if (method == NULL) { + method = "rc4-md5"; + } + + if (timeout == NULL) { + timeout = "60"; + } + +#ifdef HAVE_SETRLIMIT + /* + * no need to check the return value here since we will show + * the user an error message if setrlimit(2) fails + */ + if (nofile > 1024) { + if (verbose) { + LOGI("setting NOFILE to %d", nofile); + } + set_nofile(nofile); + } +#endif + + if (pid_flags) { + USE_SYSLOG(argv[0]); + daemonize(pid_path); + } + + if (ipv6first) { + LOGI("resolving hostname to IPv6 address first"); + } + + if (fast_open == 1) { +#ifdef TCP_FASTOPEN + LOGI("using tcp fast open"); +#else + LOGE("tcp fast open is not supported by this environment"); + fast_open = 0; +#endif + } + + if (auth) { + LOGI("onetime authentication enabled"); + } + + if (mode != TCP_ONLY) { + LOGI("UDP relay enabled"); + } + + if (mode == UDP_ONLY) { + LOGI("TCP relay disabled"); + } + +#ifdef __MINGW32__ + winsock_init(); +#else + // ignore SIGPIPE + signal(SIGPIPE, SIG_IGN); + signal(SIGCHLD, SIG_IGN); + signal(SIGABRT, SIG_IGN); +#endif + + struct ev_signal sigint_watcher; + struct ev_signal sigterm_watcher; + ev_signal_init(&sigint_watcher, signal_cb, SIGINT); + ev_signal_init(&sigterm_watcher, signal_cb, SIGTERM); + ev_signal_start(EV_DEFAULT, &sigint_watcher); + ev_signal_start(EV_DEFAULT, &sigterm_watcher); + + // setup keys + LOGI("initializing ciphers... %s", method); + int m = enc_init(password, method); + + // initialize ev loop + struct ev_loop *loop = EV_DEFAULT; + + // setup udns + if (nameserver_num == 0) { +#ifdef __MINGW32__ + nameservers[nameserver_num++] = "8.8.8.8"; + resolv_init(loop, nameservers, nameserver_num, ipv6first); +#else + resolv_init(loop, NULL, 0, ipv6first); +#endif + } else { + resolv_init(loop, nameservers, nameserver_num, ipv6first); + } + + for (int i = 0; i < nameserver_num; i++) + LOGI("using nameserver: %s", nameservers[i]); + + // initialize listen context + listen_ctx_t listen_ctx_list[server_num]; + + // bind to each interface + while (server_num > 0) { + int index = --server_num; + const char *host = server_host[index]; + + if (mode != UDP_ONLY) { + // Bind to port + int listenfd; + listenfd = create_and_bind(host, server_port, mptcp); + if (listenfd == -1) { + FATAL("bind() error"); + } + if (listen(listenfd, SSMAXCONN) == -1) { + FATAL("listen() error"); + } + setfastopen(listenfd); + setnonblocking(listenfd); + listen_ctx_t *listen_ctx = &listen_ctx_list[index]; + + // Setup proxy context + listen_ctx->timeout = atoi(timeout); + listen_ctx->fd = listenfd; + listen_ctx->method = m; + listen_ctx->iface = iface; + + // SSR beg + listen_ctx->protocol_name = protocol; + listen_ctx->protocol_param = protocol_param; + listen_ctx->method = m; + listen_ctx->obfs_name = obfs; + listen_ctx->obfs_param = obfs_param; + listen_ctx->list_protocol_global = malloc(sizeof(void *)); + listen_ctx->list_obfs_global = malloc(sizeof(void *)); + memset(listen_ctx->list_protocol_global, 0, sizeof(void *)); + memset(listen_ctx->list_obfs_global, 0, sizeof(void *)); + // SSR end + + listen_ctx->loop = loop; + + ev_io_init(&listen_ctx->io, accept_cb, listenfd, EV_READ); + ev_io_start(loop, &listen_ctx->io); + } + + // Setup UDP + if (mode != TCP_ONLY) { + init_udprelay(server_host[index], server_port, mtu, m, + auth, atoi(timeout), iface, protocol, protocol_param); + } + + if (host && strcmp(host, ":") > 0) + LOGI("listening at [%s]:%s", host, server_port); + else + LOGI("listening at %s:%s", host ? host : "*", server_port); + } + + if (manager_address != NULL) { + ev_timer_init(&stat_update_watcher, stat_update_cb, UPDATE_INTERVAL, UPDATE_INTERVAL); + ev_timer_start(EV_DEFAULT, &stat_update_watcher); + } + + ev_timer_init(&block_list_watcher, block_list_clear_cb, UPDATE_INTERVAL, UPDATE_INTERVAL); + ev_timer_start(EV_DEFAULT, &block_list_watcher); + + // setuid + if (user != NULL && ! run_as(user)) { + FATAL("failed to switch user"); + } + +#ifndef __MINGW32__ + if (geteuid() == 0){ + LOGI("running from root user"); + } else if (firewall) { + LOGE("firewall setup requires running from root user"); + exit(-1); + } +#endif + + // init block list + init_block_list(firewall); + + // Init connections + cork_dllist_init(&connections); + + // start ev loop + ev_run(loop, 0); + + if (verbose) { + LOGI("closed gracefully"); + } + + // Free block list + free_block_list(); + + if (manager_address != NULL) { + ev_timer_stop(EV_DEFAULT, &stat_update_watcher); + } + ev_timer_stop(EV_DEFAULT, &block_list_watcher); + + // Clean up + for (int i = 0; i <= server_num; i++) { + listen_ctx_t *listen_ctx = &listen_ctx_list[i]; + if (mode != UDP_ONLY) { + ev_io_stop(loop, &listen_ctx->io); + close(listen_ctx->fd); + } + } + + if (mode != UDP_ONLY) { + free_connections(loop); + } + + if (mode != TCP_ONLY) { + free_udprelay(); + } + + resolv_shutdown(loop); + +#ifdef __MINGW32__ + winsock_cleanup(); +#endif + + ev_signal_stop(EV_DEFAULT, &sigint_watcher); + ev_signal_stop(EV_DEFAULT, &sigterm_watcher); + + return 0; +} diff --git a/shadowsocksr-libev/src/server/server.h b/shadowsocksr-libev/src/server/server.h new file mode 100644 index 000000000..4cd3cf6bf --- /dev/null +++ b/shadowsocksr-libev/src/server/server.h @@ -0,0 +1,115 @@ +/* + * server.h - Define shadowsocks server's buffers and callbacks + * + * Copyright (C) 2013 - 2016, Max Lv + * + * This file is part of the shadowsocks-libev. + * + * shadowsocks-libev is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * shadowsocks-libev 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with shadowsocks-libev; see the file COPYING. If not, see + * . + */ + +#ifndef _SERVER_H +#define _SERVER_H + +#include +#include +#include + +#include "encrypt.h" +#include "jconf.h" +#include "resolv.h" +#include "obfs.h" +#include "protocol.h" + +#include "common.h" + +typedef struct listen_ctx { + ev_io io; + int fd; + int timeout; + int method; + char *iface; + struct ev_loop *loop; + + // SSR + char *protocol_name; + char *protocol_param; + char *obfs_name; + char *obfs_param; + void **list_protocol_global; + void **list_obfs_global; +} listen_ctx_t; + +typedef struct server_ctx { + ev_io io; + ev_timer watcher; + int connected; + struct server *server; +} server_ctx_t; + +typedef struct server { + int fd; + int stage; + buffer_t *buf; + ssize_t buf_capacity; + buffer_t *header_buf; + + int auth; + struct chunk *chunk; + + struct enc_ctx *e_ctx; + struct enc_ctx *d_ctx; + struct server_ctx *recv_ctx; + struct server_ctx *send_ctx; + struct listen_ctx *listen_ctx; + struct remote *remote; + + struct ResolvQuery *query; + + struct cork_dllist_item entries; + + // SSR + obfs *protocol; + obfs *obfs; + obfs_class *protocol_plugin; + obfs_class *obfs_plugin; + int obfs_compatible_state; + int protocol_compatible_state; +} server_t; + +typedef struct query { + server_t *server; + char hostname[257]; +} query_t; + +typedef struct remote_ctx { + ev_io io; + int connected; + struct remote *remote; +} remote_ctx_t; + +typedef struct remote { + int fd; + buffer_t *buf; + ssize_t buf_capacity; + struct remote_ctx *recv_ctx; + struct remote_ctx *send_ctx; + struct server *server; + + // SSR + int remote_index; +} remote_t; + +#endif // _SERVER_H diff --git a/shadowsocksr-libev/src/server/tls.c b/shadowsocksr-libev/src/server/tls.c new file mode 100644 index 000000000..5c4221605 --- /dev/null +++ b/shadowsocksr-libev/src/server/tls.c @@ -0,0 +1,263 @@ +/* + * Copyright (c) 2011 and 2012, Dustin Lundquist + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/* + * This is a minimal TLS implementation intended only to parse the server name + * extension. This was created based primarily on Wireshark dissection of a + * TLS handshake and RFC4366. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include /* malloc() */ +#include /* strncpy() */ + +#ifndef __MINGW32__ +#include +#else +#include +#endif + +#include "tls.h" +#include "protocol.h" +#include "utils.h" + +#define SERVER_NAME_LEN 256 +#define TLS_HEADER_LEN 5 +#define TLS_HANDSHAKE_CONTENT_TYPE 0x16 +#define TLS_HANDSHAKE_TYPE_CLIENT_HELLO 0x01 + +#ifndef MIN +#define MIN(X, Y) ((X) < (Y) ? (X) : (Y)) +#endif + +extern int verbose; + +static int parse_tls_header(const char *, size_t, char **); +static int parse_extensions(const char *, size_t, char **); +static int parse_server_name_extension(const char *, size_t, char **); + +static const protocol_t tls_protocol_st = { + .default_port = 443, + .parse_packet = &parse_tls_header, +}; +const protocol_t *const tls_protocol = &tls_protocol_st; + +/* Parse a TLS packet for the Server Name Indication extension in the client + * hello handshake, returning the first servername found (pointer to static + * array) + * + * Returns: + * >=0 - length of the hostname and updates *hostname + * caller is responsible for freeing *hostname + * -1 - Incomplete request + * -2 - No Host header included in this request + * -3 - Invalid hostname pointer + * -4 - malloc failure + * < -4 - Invalid TLS client hello + */ +static int +parse_tls_header(const char *data, size_t data_len, char **hostname) +{ + char tls_content_type; + char tls_version_major; + char tls_version_minor; + size_t pos = TLS_HEADER_LEN; + size_t len; + + if (hostname == NULL) + return -3; + + /* Check that our TCP payload is at least large enough for a TLS header */ + if (data_len < TLS_HEADER_LEN) + return -1; + + /* SSL 2.0 compatible Client Hello + * + * High bit of first byte (length) and content type is Client Hello + * + * See RFC5246 Appendix E.2 + */ + if (data[0] & 0x80 && data[2] == 1) { + if (verbose) + LOGI("Received SSL 2.0 Client Hello which can not support SNI."); + return -2; + } + + tls_content_type = data[0]; + if (tls_content_type != TLS_HANDSHAKE_CONTENT_TYPE) { + if (verbose) + LOGI("Request did not begin with TLS handshake."); + return -5; + } + + tls_version_major = data[1]; + tls_version_minor = data[2]; + if (tls_version_major < 3) { + if (verbose) + LOGI("Received SSL %d.%d handshake which can not support SNI.", + tls_version_major, tls_version_minor); + + return -2; + } + + /* TLS record length */ + len = ((unsigned char)data[3] << 8) + + (unsigned char)data[4] + TLS_HEADER_LEN; + data_len = MIN(data_len, len); + + /* Check we received entire TLS record length */ + if (data_len < len) + return -1; + + /* + * Handshake + */ + if (pos + 1 > data_len) { + return -5; + } + if (data[pos] != TLS_HANDSHAKE_TYPE_CLIENT_HELLO) { + if (verbose) + LOGI("Not a client hello"); + + return -5; + } + + /* Skip past fixed length records: + * 1 Handshake Type + * 3 Length + * 2 Version (again) + * 32 Random + * to Session ID Length + */ + pos += 38; + + /* Session ID */ + if (pos + 1 > data_len) + return -5; + len = (unsigned char)data[pos]; + pos += 1 + len; + + /* Cipher Suites */ + if (pos + 2 > data_len) + return -5; + len = ((unsigned char)data[pos] << 8) + (unsigned char)data[pos + 1]; + pos += 2 + len; + + /* Compression Methods */ + if (pos + 1 > data_len) + return -5; + len = (unsigned char)data[pos]; + pos += 1 + len; + + if (pos == data_len && tls_version_major == 3 && tls_version_minor == 0) { + if (verbose) + LOGI("Received SSL 3.0 handshake without extensions"); + return -2; + } + + /* Extensions */ + if (pos + 2 > data_len) + return -5; + len = ((unsigned char)data[pos] << 8) + (unsigned char)data[pos + 1]; + pos += 2; + + if (pos + len > data_len) + return -5; + return parse_extensions(data + pos, len, hostname); +} + +static int +parse_extensions(const char *data, size_t data_len, char **hostname) +{ + size_t pos = 0; + size_t len; + + /* Parse each 4 bytes for the extension header */ + while (pos + 4 <= data_len) { + /* Extension Length */ + len = ((unsigned char)data[pos + 2] << 8) + + (unsigned char)data[pos + 3]; + + /* Check if it's a server name extension */ + if (data[pos] == 0x00 && data[pos + 1] == 0x00) { + /* There can be only one extension of each type, so we break + * our state and move p to beinnging of the extension here */ + if (pos + 4 + len > data_len) + return -5; + return parse_server_name_extension(data + pos + 4, len, hostname); + } + pos += 4 + len; /* Advance to the next extension header */ + } + /* Check we ended where we expected to */ + if (pos != data_len) + return -5; + + return -2; +} + +static int +parse_server_name_extension(const char *data, size_t data_len, + char **hostname) +{ + size_t pos = 2; /* skip server name list length */ + size_t len; + + while (pos + 3 < data_len) { + len = ((unsigned char)data[pos + 1] << 8) + + (unsigned char)data[pos + 2]; + + if (pos + 3 + len > data_len) + return -5; + + switch (data[pos]) { /* name type */ + case 0x00: /* host_name */ + *hostname = malloc(len + 1); + if (*hostname == NULL) { + ERROR("malloc() failure"); + return -4; + } + + strncpy(*hostname, data + pos + 3, len); + + (*hostname)[len] = '\0'; + + return len; + default: + if (verbose) + LOGI("Unknown server name extension name type: %d", + data[pos]); + } + pos += 3 + len; + } + /* Check we ended where we expected to */ + if (pos != data_len) + return -5; + + return -2; +} diff --git a/shadowsocksr-libev/src/server/tls.h b/shadowsocksr-libev/src/server/tls.h new file mode 100644 index 000000000..39989131f --- /dev/null +++ b/shadowsocksr-libev/src/server/tls.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2011 and 2012, Dustin Lundquist + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef TLS_H +#define TLS_H + +#include "protocol.h" + +const protocol_t *const tls_protocol; + +#endif diff --git a/shadowsocksr-libev/src/server/tls1.2_ticket.c b/shadowsocksr-libev/src/server/tls1.2_ticket.c new file mode 100644 index 000000000..88970c09b --- /dev/null +++ b/shadowsocksr-libev/src/server/tls1.2_ticket.c @@ -0,0 +1,609 @@ + +#include "tls1.2_ticket.h" +#include "list.c" + +typedef struct tls12_ticket_auth_global_data { + uint8_t local_client_id[32]; + List client_data; + time_t startup_time; +}tls12_ticket_auth_global_data; + +typedef struct tls12_ticket_auth_local_data { + int handshake_status; + char *send_buffer; + int send_buffer_size; + char *recv_buffer; + int recv_buffer_size; +}tls12_ticket_auth_local_data; + +void tls12_ticket_auth_local_data_init(tls12_ticket_auth_local_data* local) { + local->handshake_status = 0; + local->send_buffer = malloc(0); + local->send_buffer_size = 0; + local->recv_buffer = malloc(0); + local->recv_buffer_size = 0; +} + +void * tls12_ticket_auth_init_data() { + tls12_ticket_auth_global_data *global = (tls12_ticket_auth_global_data*)malloc(sizeof(tls12_ticket_auth_global_data)); + rand_bytes(global->local_client_id, 32); + global->client_data = list_init(22); + global->startup_time = time(NULL); + return global; +} + +obfs * tls12_ticket_auth_new_obfs() { + obfs * self = new_obfs(); + self->l_data = malloc(sizeof(tls12_ticket_auth_local_data)); + tls12_ticket_auth_local_data_init((tls12_ticket_auth_local_data*)self->l_data); + return self; +} + +void tls12_ticket_auth_dispose(obfs *self) { + tls12_ticket_auth_local_data *local = (tls12_ticket_auth_local_data*)self->l_data; + if (local->send_buffer != NULL) { + free(local->send_buffer); + local->send_buffer = NULL; + } + if (local->recv_buffer != NULL) { + free(local->recv_buffer); + local->recv_buffer = NULL; + } + free(local); + dispose_obfs(self); +} + +int tls12_ticket_pack_auth_data(tls12_ticket_auth_global_data *global, server_info *server, char *outdata) { + int out_size = 32; + time_t t = time(NULL); + outdata[0] = t >> 24; + outdata[1] = t >> 16; + outdata[2] = t >> 8; + outdata[3] = t; + rand_bytes((uint8_t*)outdata + 4, 18); + + uint8_t *key = (uint8_t*)malloc(server->key_len + 32); + char hash[ONETIMEAUTH_BYTES * 2]; + memcpy(key, server->key, server->key_len); + memcpy(key + server->key_len, global->local_client_id, 32); + ss_sha1_hmac_with_key(hash, outdata, out_size - OBFS_HMAC_SHA1_LEN, key, server->key_len + 32); + free(key); + memcpy(outdata + out_size - OBFS_HMAC_SHA1_LEN, hash, OBFS_HMAC_SHA1_LEN); + return out_size; +} + +void tls12_ticket_auth_pack_data(char *encryptdata, int datalength, int start, int len, char *out_buffer, int outlength) { + out_buffer[outlength] = 0x17; + out_buffer[outlength + 1] = 0x3; + out_buffer[outlength + 2] = 0x3; + out_buffer[outlength + 3] = len >> 8; + out_buffer[outlength + 4] = len; + memcpy(out_buffer + outlength + 5, encryptdata + start, len); +} + +int tls12_ticket_auth_client_encode(obfs *self, char **pencryptdata, int datalength, size_t* capacity) { + char *encryptdata = *pencryptdata; + tls12_ticket_auth_local_data *local = (tls12_ticket_auth_local_data*)self->l_data; + tls12_ticket_auth_global_data *global = (tls12_ticket_auth_global_data*)self->server.g_data; + char * out_buffer = NULL; + + if (local->handshake_status == 8) { + if (datalength < 1024) { + if (*capacity < datalength + 5) { + *pencryptdata = (char*)realloc(*pencryptdata, *capacity = (datalength + 5) * 2); + encryptdata = *pencryptdata; + } + memmove(encryptdata + 5, encryptdata, datalength); + encryptdata[0] = 0x17; + encryptdata[1] = 0x3; + encryptdata[2] = 0x3; + encryptdata[3] = datalength >> 8; + encryptdata[4] = datalength; + return datalength + 5; + } else { + out_buffer = (char*)malloc(datalength + 2048); + int start = 0; + int outlength = 0; + int len; + while (datalength - start > 2048) { + len = xorshift128plus() % 4096 + 100; + if (len > datalength - start) + len = datalength - start; + tls12_ticket_auth_pack_data(encryptdata, datalength, start, len, out_buffer, outlength); + outlength += len + 5; + start += len; + } + if (datalength - start > 0) { + len = datalength - start; + tls12_ticket_auth_pack_data(encryptdata, datalength, start, len, out_buffer, outlength); + outlength += len + 5; + } + if (*capacity < outlength) { + *pencryptdata = (char*)realloc(*pencryptdata, *capacity = outlength * 2); + encryptdata = *pencryptdata; + } + memcpy(encryptdata, out_buffer, outlength); + free(out_buffer); + return outlength; + } + } + local->send_buffer = (char*)realloc(local->send_buffer, local->send_buffer_size + datalength + 5); + memcpy(local->send_buffer + local->send_buffer_size + 5, encryptdata, datalength); + local->send_buffer[local->send_buffer_size] = 0x17; + local->send_buffer[local->send_buffer_size + 1] = 0x3; + local->send_buffer[local->send_buffer_size + 2] = 0x3; + local->send_buffer[local->send_buffer_size + 3] = datalength >> 8; + local->send_buffer[local->send_buffer_size + 4] = datalength; + local->send_buffer_size += datalength + 5; + + if (local->handshake_status == 0) { +#define CSTR_DECL(name, len, str) const char* name = str; const int len = sizeof(str) - 1; + CSTR_DECL(tls_data0, tls_data0_len, "\x00\x1c\xc0\x2b\xc0\x2f\xcc\xa9\xcc\xa8\xcc\x14\xcc\x13\xc0\x0a\xc0\x14\xc0\x09\xc0\x13\x00\x9c\x00\x35\x00\x2f\x00\x0a\x01\x00" + ); + CSTR_DECL(tls_data1, tls_data1_len, "\xff\x01\x00\x01\x00" + ); + CSTR_DECL(tls_data2, tls_data2_len, "\x00\x17\x00\x00\x00\x23\x00\xd0"); + CSTR_DECL(tls_data3, tls_data3_len, "\x00\x0d\x00\x16\x00\x14\x06\x01\x06\x03\x05\x01\x05\x03\x04\x01\x04\x03\x03\x01\x03\x03\x02\x01\x02\x03\x00\x05\x00\x05\x01\x00\x00\x00\x00\x00\x12\x00\x00\x75\x50\x00\x00\x00\x0b\x00\x02\x01\x00\x00\x0a\x00\x06\x00\x04\x00\x17\x00\x18" + //"00150066000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" // padding + ); + uint8_t tls_data[2048]; + int tls_data_len = 0; + memcpy(tls_data, tls_data1, tls_data1_len); + tls_data_len += tls_data1_len; + + char hosts[1024]; + char * phost[128]; + int host_num = 0; + int pos; + + char sni[256] = {0}; + if (self->server.param && strlen(self->server.param) == 0) + self->server.param = NULL; + strncpy(hosts, self->server.param ? self->server.param : self->server.host, sizeof hosts); + phost[host_num++] = hosts; + for (pos = 0; hosts[pos]; ++pos) { + if (hosts[pos] == ',') { + phost[host_num++] = &hosts[pos + 1]; + } + } + host_num = xorshift128plus() % host_num; + + sprintf(sni, "%s", phost[host_num]); + int sni_len = strlen(sni); + if (sni_len > 0 && sni[sni_len - 1] >= '0' && sni[sni_len - 1] <= '9') + sni_len = 0; + tls_data[tls_data_len] = '\0'; + tls_data[tls_data_len + 1] = '\0'; + tls_data[tls_data_len + 2] = (sni_len + 5) >> 8; + tls_data[tls_data_len + 3] = (sni_len + 5); + tls_data[tls_data_len + 4] = (sni_len + 3) >> 8; + tls_data[tls_data_len + 5] = (sni_len + 3); + tls_data[tls_data_len + 6] = '\0'; + tls_data[tls_data_len + 7] = sni_len >> 8; + tls_data[tls_data_len + 8] = sni_len; + memcpy(tls_data + tls_data_len + 9, sni, sni_len); + tls_data_len += 9 + sni_len; + memcpy(tls_data + tls_data_len, tls_data2, tls_data2_len); + tls_data_len += tls_data2_len; + rand_bytes(tls_data + tls_data_len, 208); + tls_data_len += 208; + memcpy(tls_data + tls_data_len, tls_data3, tls_data3_len); + tls_data_len += tls_data3_len; + + datalength = 11 + 32 + 1 + 32 + tls_data0_len + 2 + tls_data_len; + out_buffer = (char*)malloc(datalength); + char *pdata = out_buffer + datalength - tls_data_len; + int len = tls_data_len; + memcpy(pdata, tls_data, tls_data_len); + pdata[-1] = tls_data_len; + pdata[-2] = tls_data_len >> 8; + pdata -= 2; len += 2; + memcpy(pdata - tls_data0_len, tls_data0, tls_data0_len); + pdata -= tls_data0_len; len += tls_data0_len; + memcpy(pdata - 32, global->local_client_id, 32); + pdata -= 32; len += 32; + pdata[-1] = 0x20; + pdata -= 1; len += 1; + tls12_ticket_pack_auth_data(global, &self->server, pdata - 32); + pdata -= 32; len += 32; + pdata[-1] = 0x3; + pdata[-2] = 0x3; // tls version + pdata -= 2; len += 2; + pdata[-1] = len; + pdata[-2] = len >> 8; + pdata[-3] = 0; + pdata[-4] = 1; + pdata -= 4; len += 4; + + pdata[-1] = len; + pdata[-2] = len >> 8; + pdata -= 2; len += 2; + pdata[-1] = 0x1; + pdata[-2] = 0x3; // tls version + pdata -= 2; len += 2; + pdata[-1] = 0x16; // tls handshake + pdata -= 1; len += 1; + + local->handshake_status = 1; + } else if (datalength == 0) { + datalength = local->send_buffer_size + 43; + out_buffer = (char*)malloc(datalength); + char *pdata = out_buffer; + memcpy(pdata, "\x14\x03\x03\x00\x01\x01", 6); + pdata += 6; + memcpy(pdata, "\x16\x03\x03\x00\x20", 5); + pdata += 5; + rand_bytes((uint8_t*)pdata, 22); + pdata += 22; + + uint8_t *key = (uint8_t*)malloc(self->server.key_len + 32); + char hash[ONETIMEAUTH_BYTES * 2]; + memcpy(key, self->server.key, self->server.key_len); + memcpy(key + self->server.key_len, global->local_client_id, 32); + ss_sha1_hmac_with_key(hash, out_buffer, pdata - out_buffer, key, self->server.key_len + 32); + free(key); + memcpy(pdata, hash, OBFS_HMAC_SHA1_LEN); + + pdata += OBFS_HMAC_SHA1_LEN; + memcpy(pdata, local->send_buffer, local->send_buffer_size); + free(local->send_buffer); + local->send_buffer = NULL; + + local->handshake_status = 8; + } else { + return 0; + } + if (*capacity < datalength) { + *pencryptdata = (char*)realloc(*pencryptdata, *capacity = datalength * 2); + encryptdata = *pencryptdata; + } + memmove(encryptdata, out_buffer, datalength); + free(out_buffer); + return datalength; +} + +int tls12_ticket_auth_server_encode(obfs *self, char **pencryptdata, int datalength, size_t* capacity) { + char *encryptdata = *pencryptdata; + tls12_ticket_auth_local_data *local = (tls12_ticket_auth_local_data*)self->l_data; + tls12_ticket_auth_global_data *global = (tls12_ticket_auth_global_data*)self->server.g_data; + char * out_buffer = NULL; + + if (local->handshake_status == 8) { + if (datalength < 1024) { + if (*capacity < datalength + 5) { + *pencryptdata = (char*)realloc(*pencryptdata, *capacity = (datalength + 5) * 2); + encryptdata = *pencryptdata; + } + memmove(encryptdata + 5, encryptdata, datalength); + encryptdata[0] = 0x17; + encryptdata[1] = 0x3; + encryptdata[2] = 0x3; + encryptdata[3] = datalength >> 8; + encryptdata[4] = datalength; + return datalength + 5; + } else { + out_buffer = (char*)malloc(datalength + 2048); + int start = 0; + int outlength = 0; + int len; + while (datalength - start > 2048) { + len = xorshift128plus() % 4096 + 100; + if (len > datalength - start) + len = datalength - start; + tls12_ticket_auth_pack_data(encryptdata, datalength, start, len, out_buffer, outlength); + outlength += len + 5; + start += len; + } + if (datalength - start > 0) { + len = datalength - start; + tls12_ticket_auth_pack_data(encryptdata, datalength, start, len, out_buffer, outlength); + outlength += len + 5; + } + if (*capacity < outlength) { + *pencryptdata = (char*)realloc(*pencryptdata, *capacity = outlength * 2); + encryptdata = *pencryptdata; + } + memcpy(encryptdata, out_buffer, outlength); + free(out_buffer); + return outlength; + } + } + + local->handshake_status = 3; + + out_buffer = (char*)malloc(43 + 86); + int data_len = 0; + char *p_data = out_buffer + 86; + + memcpy(p_data - 10, "\xc0\x2f\x00\x00\x05\xff\x01\x00\x01\x00", 10); + p_data -= 10;data_len += 10; + + memcpy(p_data - 32, global->local_client_id, 32); + p_data -= 32;data_len += 32; + + p_data[-1] = 0x20; + p_data -= 1;data_len += 1; + + tls12_ticket_pack_auth_data(global, &self->server, p_data - 32); + p_data -= 32;data_len += 32; + + p_data[-1] = 0x3; + p_data[-2] = 0x3; // tls version + p_data -= 2;data_len += 2; + + p_data[-1] = data_len; + p_data[-2] = data_len >> 8; + p_data[-3] = 0x00; + p_data[-4] = 0x02; + p_data -= 4; data_len += 4; + + p_data[-1] = data_len; + p_data[-2] = data_len >> 8; + p_data[-3] = 0x03; + p_data[-4] = 0x03; + p_data[-5] = 0x16; + p_data -= 5; data_len += 5; + + memcpy(out_buffer, p_data, data_len); + char *pdata = out_buffer + 86; + + memcpy(pdata, "\x14\x03\x03\x00\x01\x01", 6); + pdata += 6; + memcpy(pdata, "\x16\x03\x03\x00\x20", 5); + pdata += 5; + rand_bytes((uint8_t*)pdata, 22); + pdata += 22; + + uint8_t *key = (uint8_t*)malloc(self->server.key_len + 32); + char hash[ONETIMEAUTH_BYTES * 2]; + memcpy(key, self->server.key, self->server.key_len); + memcpy(key + self->server.key_len, global->local_client_id, 32); + ss_sha1_hmac_with_key(hash, out_buffer, 43 + 86, key, self->server.key_len + 32); + free(key); + memcpy(pdata, hash, OBFS_HMAC_SHA1_LEN); + + memmove(encryptdata, out_buffer, 43 + 86); + free(out_buffer); + return 43 + 86; +} + +int tls12_ticket_auth_client_decode(obfs *self, char **pencryptdata, int datalength, size_t* capacity, int *needsendback) { + char *encryptdata = *pencryptdata; + tls12_ticket_auth_local_data *local = (tls12_ticket_auth_local_data*)self->l_data; + tls12_ticket_auth_global_data *global = (tls12_ticket_auth_global_data*)self->server.g_data; + + *needsendback = 0; + + if (local->handshake_status == 8) { + local->recv_buffer_size += datalength; + local->recv_buffer = (char*)realloc(local->recv_buffer, local->recv_buffer_size); + memcpy(local->recv_buffer + local->recv_buffer_size - datalength, encryptdata, datalength); + datalength = 0; + while (local->recv_buffer_size > 5) { + if (local->recv_buffer[0] != 0x17) + return -1; + int size = ((int)(unsigned char)local->recv_buffer[3] << 8) + (unsigned char)local->recv_buffer[4]; + if (size + 5 > local->recv_buffer_size) + break; + if (*capacity < datalength + size) { + *pencryptdata = (char*)realloc(*pencryptdata, *capacity = (datalength + size) * 2); + encryptdata = *pencryptdata; + } + memcpy(encryptdata + datalength, local->recv_buffer + 5, size); + datalength += size; + local->recv_buffer_size -= 5 + size; + memmove(local->recv_buffer, local->recv_buffer + 5 + size, local->recv_buffer_size); + } + return datalength; + } + if (datalength < 11 + 32 + 1 + 32) { + return -1; + } + + uint8_t *key = (uint8_t*)malloc(self->server.key_len + 32); + char hash[ONETIMEAUTH_BYTES * 2]; + memcpy(key, self->server.key, self->server.key_len); + memcpy(key + self->server.key_len, global->local_client_id, 32); + ss_sha1_hmac_with_key(hash, encryptdata + 11, 22, key, self->server.key_len + 32); + free(key); + + if (memcmp(encryptdata + 33, hash, OBFS_HMAC_SHA1_LEN)) { + return -1; + } + + *needsendback = 1; + return 0; +} + +int tls12_ticket_auth_server_decode(obfs *self, char **pencryptdata, int datalength, size_t* capacity, int *needsendback) { + char *encryptdata = *pencryptdata; + tls12_ticket_auth_local_data *local = (tls12_ticket_auth_local_data*)self->l_data; + tls12_ticket_auth_global_data *global = (tls12_ticket_auth_global_data*)self->server.g_data; + + *needsendback = 0; + + if (local->handshake_status == 8) { + if(datalength != 0) + { + local->recv_buffer = (char*)realloc(local->recv_buffer, local->recv_buffer_size + datalength); + memmove(local->recv_buffer + local->recv_buffer_size, encryptdata, datalength); + local->recv_buffer_size += datalength; + } + datalength = 0; + + while (local->recv_buffer_size > 5) { + if (local->recv_buffer[0] != 0x17 || local->recv_buffer[1] != 0x03 || local->recv_buffer[2] != 0x03) + { + LOGE("server_decode data error, wrong tls version 3"); + return -1; + } + int size = ((int)(unsigned char)local->recv_buffer[3] << 8) + (unsigned char)local->recv_buffer[4]; + if (size + 5 > local->recv_buffer_size) + break; + if (*capacity < local->recv_buffer_size + size) { + *pencryptdata = (char*)realloc(*pencryptdata, *capacity = (local->recv_buffer_size + size) * 2); + encryptdata = *pencryptdata; + } + memcpy(encryptdata + datalength, local->recv_buffer + 5, size); + datalength += size; + local->recv_buffer_size -= 5 + size; + memmove(local->recv_buffer, local->recv_buffer + 5 + size, local->recv_buffer_size); + } + return datalength; + } + + if (local->handshake_status == 3) { + + char *verify = encryptdata; + + if(datalength < 43) + { + LOGE("server_decode data error, too short:%d", (int)datalength); + return -1; + } + + if(encryptdata[0] != 0x14 || encryptdata[1] != 0x03 || encryptdata[2] != 0x03 || encryptdata[3] != 0x00 || encryptdata[4] != 0x01 || encryptdata[5] != 0x01) + { + LOGE("server_decode data error, wrong tls version"); + return -1; + } + + encryptdata += 6; + + if(encryptdata[0] != 0x16 || encryptdata[1] != 0x03 || encryptdata[2] != 0x03 || encryptdata[3] != 0x00 || encryptdata[4] != 0x20) + { + LOGE("server_decode data error, wrong tls version 2"); + return -1; + } + + uint8_t *key = (uint8_t*)malloc(self->server.key_len + 32); + char hash[ONETIMEAUTH_BYTES * 2]; + memcpy(key, self->server.key, self->server.key_len); + memcpy(key + self->server.key_len, global->local_client_id, 32); + ss_sha1_hmac_with_key(hash, verify, 33, key, self->server.key_len + 32); + free(key); + + if (memcmp(verify + 33, hash, OBFS_HMAC_SHA1_LEN) != 0) { + LOGE("server_decode data error, hash Mismatch %d",(int)memcmp(verify + 33, hash, OBFS_HMAC_SHA1_LEN)); + return -1; + } + + local->recv_buffer_size = datalength - 43; + local->recv_buffer = (char*)realloc(local->recv_buffer, local->recv_buffer_size); + memmove(local->recv_buffer, encryptdata + 37, datalength - 43); + + local->handshake_status = 8; + return tls12_ticket_auth_server_decode(self, pencryptdata, 0, capacity, needsendback); + } + + local->handshake_status = 2; + if(encryptdata[0] != 0x16 || encryptdata[1] != 0x03 || encryptdata[2] != 0x01) + { + return -1; + } + + encryptdata += 3; + + { + int size = ((int)(unsigned char)encryptdata[0] << 8) + (unsigned char)encryptdata[1]; + if(size != datalength - 5) + { + LOGE("tls_auth wrong tls head size"); + return -1; + } + } + + encryptdata += 2; + + if(encryptdata[0] != 0x01 || encryptdata[1] != 0x00) + { + LOGE("tls_auth not client hello message"); + return -1; + } + + encryptdata += 2; + + { + int size = ((int)(unsigned char)encryptdata[0] << 8) + (unsigned char)encryptdata[1]; + if(size != datalength - 9) + { + LOGE("tls_auth wrong message size"); + return -1; + } + } + + encryptdata += 2; + + if(encryptdata[0] != 0x03 || encryptdata[1] != 0x03) + { + LOGE("tls_auth wrong tls version"); + return -1; + } + + encryptdata += 2; + + char *verifyid = encryptdata; + + encryptdata += 32; + + int sessionid_len = encryptdata[0]; + if(sessionid_len < 32) + { + LOGE("tls_auth wrong sessionid_len"); + return -1; + } + + char *sessionid = encryptdata + 1; + memcpy(global->local_client_id , sessionid, sessionid_len); + + uint8_t *key = (uint8_t*)malloc(self->server.key_len + sessionid_len); + char hash[ONETIMEAUTH_BYTES * 2]; + memcpy(key, self->server.key, self->server.key_len); + memcpy(key + self->server.key_len, global->local_client_id, sessionid_len); + ss_sha1_hmac_with_key(hash, verifyid, 22, key, self->server.key_len + sessionid_len); + free(key); + + encryptdata += (sessionid_len + 1); + + long utc_time = ((int)(unsigned char)verifyid[0] << 24) + ((int)(unsigned char)verifyid[1] << 16) + ((int)(unsigned char)verifyid[2] << 8) + (unsigned char)verifyid[3]; + time_t t = time(NULL); + + + if (self->server.param && strlen(self->server.param) == 0) + { + self->server.param = NULL; + } + + int max_time_dif = 0; + int time_dif = utc_time - t; + if(self->server.param) + { + max_time_dif = atoi(self->server.param); + } + + if(max_time_dif > 0 && (time_dif < -max_time_dif || time_dif > max_time_dif || utc_time - global->startup_time < -max_time_dif / 2)) + { + LOGE("tls_auth wrong time"); + return -1; + } + + if (memcmp(verifyid + 22, hash, OBFS_HMAC_SHA1_LEN)) { + LOGE("tls_auth wrong sha1"); + return -1; + } + + int search_result = global->client_data->have_same_cmp(global->client_data, verifyid); + if(search_result != 0) + { + LOGE("replay attack detect!"); + return -1; + } + + global->client_data->add_back(global->client_data, verifyid); + + encryptdata += 48; + + *needsendback = 1; + + return 0; +} diff --git a/shadowsocksr-libev/src/server/tls1.2_ticket.h b/shadowsocksr-libev/src/server/tls1.2_ticket.h new file mode 100644 index 000000000..10a57c907 --- /dev/null +++ b/shadowsocksr-libev/src/server/tls1.2_ticket.h @@ -0,0 +1,20 @@ +/* + * http_simple.h - Define shadowsocksR server's buffers and callbacks + * + * Copyright (C) 2015 - 2016, Break Wa11 + */ + +#ifndef _TLS1_2_TICKET_H +#define _TLS1_2_TICKET_H + +void * tls12_ticket_auth_init_data(); +obfs * tls12_ticket_auth_new_obfs(); +void tls12_ticket_auth_dispose(obfs *self); + +int tls12_ticket_auth_client_encode(obfs *self, char **pencryptdata, int datalength, size_t* capacity); +int tls12_ticket_auth_client_decode(obfs *self, char **pencryptdata, int datalength, size_t* capacity, int *needsendback); + +int tls12_ticket_auth_server_encode(obfs *self, char **pencryptdata, int datalength, size_t* capacity); +int tls12_ticket_auth_server_decode(obfs *self, char **pencryptdata, int datalength, size_t* capacity, int *needsendback); + +#endif // _TLS1_2_TICKET_H diff --git a/shadowsocksr-libev/src/server/udprelay.c b/shadowsocksr-libev/src/server/udprelay.c new file mode 100644 index 000000000..d9251eeae --- /dev/null +++ b/shadowsocksr-libev/src/server/udprelay.c @@ -0,0 +1,1452 @@ +/* + * udprelay.c - Setup UDP relay for both client and server + * + * Copyright (C) 2013 - 2016, Max Lv + * + * This file is part of the shadowsocks-libev. + * + * shadowsocks-libev is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * shadowsocks-libev 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with shadowsocks-libev; see the file COPYING. If not, see + * . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef __MINGW32__ +#include +#include +#include +#include +#include +#endif + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#if defined(HAVE_SYS_IOCTL_H) && defined(HAVE_NET_IF_H) && defined(__linux__) +#include +#include +#define SET_INTERFACE +#endif + +#ifdef __MINGW32__ +#include "win32.h" +#endif + +#include +#include + +#include "utils.h" +#include "netutils.h" +#include "cache.h" +#include "udprelay.h" + +#ifdef MODULE_REMOTE +#define MAX_UDP_CONN_NUM 512 +#else +#define MAX_UDP_CONN_NUM 256 +#endif + +#ifdef MODULE_REMOTE +#ifdef MODULE_ +#error "MODULE_REMOTE and MODULE_LOCAL should not be both defined" +#endif +#endif + +#ifndef EAGAIN +#define EAGAIN EWOULDBLOCK +#endif + +#ifndef EWOULDBLOCK +#define EWOULDBLOCK EAGAIN +#endif + +static void server_recv_cb(EV_P_ ev_io *w, int revents); +static void remote_recv_cb(EV_P_ ev_io *w, int revents); +static void remote_timeout_cb(EV_P_ ev_timer *watcher, int revents); + +static char *hash_key(const int af, const struct sockaddr_storage *addr); +#ifdef MODULE_REMOTE +static void query_resolve_cb(struct sockaddr *addr, void *data); +#endif +static void close_and_free_remote(EV_P_ remote_ctx_t *ctx); +static remote_ctx_t *new_remote(int fd, server_ctx_t *server_ctx); + +#ifdef ANDROID +extern uint64_t tx; +extern uint64_t rx; +extern int vpn; +#endif + +extern int verbose; +#ifdef MODULE_REMOTE +extern uint64_t tx; +extern uint64_t rx; +#endif + +static int packet_size = DEFAULT_PACKET_SIZE; +static int buf_size = DEFAULT_PACKET_SIZE * 2; +static int server_num = 0; +static server_ctx_t *server_ctx_list[MAX_REMOTE_NUM] = { NULL }; + +#ifndef __MINGW32__ +static int +setnonblocking(int fd) +{ + int flags; + if (-1 == (flags = fcntl(fd, F_GETFL, 0))) { + flags = 0; + } + return fcntl(fd, F_SETFL, flags | O_NONBLOCK); +} + +#endif + +#if defined(MODULE_REMOTE) && defined(SO_BROADCAST) +static int +set_broadcast(int socket_fd) +{ + int opt = 1; + return setsockopt(socket_fd, SOL_SOCKET, SO_BROADCAST, &opt, sizeof(opt)); +} + +#endif + +#ifdef SO_NOSIGPIPE +static int +set_nosigpipe(int socket_fd) +{ + int opt = 1; + return setsockopt(socket_fd, SOL_SOCKET, SO_NOSIGPIPE, &opt, sizeof(opt)); +} + +#endif + +#ifdef MODULE_REDIR + +#ifndef IP_TRANSPARENT +#define IP_TRANSPARENT 19 +#endif + +#ifndef IP_RECVORIGDSTADDR +#define IP_RECVORIGDSTADDR 20 +#endif + +static int +get_dstaddr(struct msghdr *msg, struct sockaddr_storage *dstaddr) +{ + struct cmsghdr *cmsg; + + for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) { + if (cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_RECVORIGDSTADDR) { + memcpy(dstaddr, CMSG_DATA(cmsg), sizeof(struct sockaddr_in)); + dstaddr->ss_family = AF_INET; + return 0; + } else if (cmsg->cmsg_level == SOL_IPV6 && cmsg->cmsg_type == IP_RECVORIGDSTADDR) { + memcpy(dstaddr, CMSG_DATA(cmsg), sizeof(struct sockaddr_in6)); + dstaddr->ss_family = AF_INET6; + return 0; + } + } + + return 1; +} + +#endif + +#define HASH_KEY_LEN sizeof(struct sockaddr_storage) + sizeof(int) +static char * +hash_key(const int af, const struct sockaddr_storage *addr) +{ + size_t addr_len = sizeof(struct sockaddr_storage); + static char key[HASH_KEY_LEN]; + + memset(key, 0, HASH_KEY_LEN); + memcpy(key, &af, sizeof(int)); + memcpy(key + sizeof(int), (const uint8_t *)addr, addr_len); + + return key; +} + +#if defined(MODULE_REDIR) || defined(MODULE_REMOTE) +static int +construct_udprealy_header(const struct sockaddr_storage *in_addr, + char *addr_header) +{ + int addr_header_len = 0; + if (in_addr->ss_family == AF_INET) { + struct sockaddr_in *addr = (struct sockaddr_in *)in_addr; + size_t addr_len = sizeof(struct in_addr); + addr_header[addr_header_len++] = 1; + memcpy(addr_header + addr_header_len, &addr->sin_addr, addr_len); + addr_header_len += addr_len; + memcpy(addr_header + addr_header_len, &addr->sin_port, 2); + addr_header_len += 2; + } else if (in_addr->ss_family == AF_INET6) { + struct sockaddr_in6 *addr = (struct sockaddr_in6 *)in_addr; + size_t addr_len = sizeof(struct in6_addr); + addr_header[addr_header_len++] = 4; + memcpy(addr_header + addr_header_len, &addr->sin6_addr, addr_len); + addr_header_len += addr_len; + memcpy(addr_header + addr_header_len, &addr->sin6_port, 2); + addr_header_len += 2; + } else { + return 0; + } + return addr_header_len; +} + +#endif + +static int +parse_udprealy_header(const char *buf, const size_t buf_len, + char *host, char *port, struct sockaddr_storage *storage) +{ + const uint8_t atyp = *(uint8_t *)buf; + int offset = 1; + + // get remote addr and port + if ((atyp & ADDRTYPE_MASK) == 1) { + // IP V4 + size_t in_addr_len = sizeof(struct in_addr); + if (buf_len >= in_addr_len + 3) { + if (storage != NULL) { + struct sockaddr_in *addr = (struct sockaddr_in *)storage; + addr->sin_family = AF_INET; + addr->sin_addr = *(struct in_addr *)(buf + offset); + addr->sin_port = *(uint16_t *)(buf + offset + in_addr_len); + } + if (host != NULL) { + dns_ntop(AF_INET, (const void *)(buf + offset), + host, INET_ADDRSTRLEN); + } + offset += in_addr_len; + } + } else if ((atyp & ADDRTYPE_MASK) == 3) { + // Domain name + uint8_t name_len = *(uint8_t *)(buf + offset); + if (name_len + 4 <= buf_len) { + if (storage != NULL) { + char tmp[257] = { 0 }; + struct cork_ip ip; + memcpy(tmp, buf + offset + 1, name_len); + if (cork_ip_init(&ip, tmp) != -1) { + if (ip.version == 4) { + struct sockaddr_in *addr = (struct sockaddr_in *)storage; + dns_pton(AF_INET, tmp, &(addr->sin_addr)); + addr->sin_port = *(uint16_t *)(buf + offset + 1 + name_len); + addr->sin_family = AF_INET; + } else if (ip.version == 6) { + struct sockaddr_in6 *addr = (struct sockaddr_in6 *)storage; + dns_pton(AF_INET, tmp, &(addr->sin6_addr)); + addr->sin6_port = *(uint16_t *)(buf + offset + 1 + name_len); + addr->sin6_family = AF_INET6; + } + } + } + if (host != NULL) { + memcpy(host, buf + offset + 1, name_len); + } + offset += 1 + name_len; + } + } else if ((atyp & ADDRTYPE_MASK) == 4) { + // IP V6 + size_t in6_addr_len = sizeof(struct in6_addr); + if (buf_len >= in6_addr_len + 3) { + if (storage != NULL) { + struct sockaddr_in6 *addr = (struct sockaddr_in6 *)storage; + addr->sin6_family = AF_INET6; + addr->sin6_addr = *(struct in6_addr *)(buf + offset); + addr->sin6_port = *(uint16_t *)(buf + offset + in6_addr_len); + } + if (host != NULL) { + dns_ntop(AF_INET6, (const void *)(buf + offset), + host, INET6_ADDRSTRLEN); + } + offset += in6_addr_len; + } + } + + if (offset == 1) { + LOGE("[udp] invalid header with addr type %d", atyp); + return 0; + } + + if (port != NULL) { + sprintf(port, "%d", ntohs(*(uint16_t *)(buf + offset))); + } + offset += 2; + + return offset; +} + +static char * +get_addr_str(const struct sockaddr *sa) +{ + static char s[SS_ADDRSTRLEN]; + memset(s, 0, SS_ADDRSTRLEN); + char addr[INET6_ADDRSTRLEN] = { 0 }; + char port[PORTSTRLEN] = { 0 }; + uint16_t p; + + switch (sa->sa_family) { + case AF_INET: + dns_ntop(AF_INET, &(((struct sockaddr_in *)sa)->sin_addr), + addr, INET_ADDRSTRLEN); + p = ntohs(((struct sockaddr_in *)sa)->sin_port); + sprintf(port, "%d", p); + break; + + case AF_INET6: + dns_ntop(AF_INET6, &(((struct sockaddr_in6 *)sa)->sin6_addr), + addr, INET6_ADDRSTRLEN); + p = ntohs(((struct sockaddr_in *)sa)->sin_port); + sprintf(port, "%d", p); + break; + + default: + strncpy(s, "Unknown AF", SS_ADDRSTRLEN); + } + + int addr_len = strlen(addr); + int port_len = strlen(port); + memcpy(s, addr, addr_len); + memcpy(s + addr_len + 1, port, port_len); + s[addr_len] = ':'; + + return s; +} + +int +create_remote_socket(int ipv6) +{ + int remote_sock; + + if (ipv6) { + // Try to bind IPv6 first + struct sockaddr_in6 addr; + memset(&addr, 0, sizeof(struct sockaddr_in6)); + addr.sin6_family = AF_INET6; + addr.sin6_addr = in6addr_any; + addr.sin6_port = 0; + remote_sock = socket(AF_INET6, SOCK_DGRAM, 0); + if (remote_sock == -1) { + ERROR("[udp] cannot create socket"); + return -1; + } + if (bind(remote_sock, (struct sockaddr *)&addr, sizeof(addr)) != 0) { + FATAL("[udp] cannot bind remote"); + return -1; + } + } else { + // Or else bind to IPv4 + struct sockaddr_in addr; + memset(&addr, 0, sizeof(struct sockaddr_in)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = INADDR_ANY; + addr.sin_port = 0; + remote_sock = socket(AF_INET, SOCK_DGRAM, 0); + if (remote_sock == -1) { + ERROR("[udp] cannot create socket"); + return -1; + } + + if (bind(remote_sock, (struct sockaddr *)&addr, sizeof(addr)) != 0) { + FATAL("[udp] cannot bind remote"); + return -1; + } + } + return remote_sock; +} + +int +create_server_socket(const char *host, const char *port) +{ + struct addrinfo hints; + struct addrinfo *result, *rp, *ipv4v6bindall; + int s, server_sock; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; /* Return IPv4 and IPv6 choices */ + hints.ai_socktype = SOCK_DGRAM; /* We want a UDP socket */ + hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG; /* For wildcard IP address */ + hints.ai_protocol = IPPROTO_UDP; + + s = getaddrinfo(host, port, &hints, &result); + if (s != 0) { + LOGE("[udp] getaddrinfo: %s", gai_strerror(s)); + return -1; + } + + rp = result; + + /* + * On Linux, with net.ipv6.bindv6only = 0 (the default), getaddrinfo(NULL) with + * AI_PASSIVE returns 0.0.0.0 and :: (in this order). AI_PASSIVE was meant to + * return a list of addresses to listen on, but it is impossible to listen on + * 0.0.0.0 and :: at the same time, if :: implies dualstack mode. + */ + if (!host) { + ipv4v6bindall = result; + + /* Loop over all address infos found until a IPV6 address is found. */ + while (ipv4v6bindall) { + if (ipv4v6bindall->ai_family == AF_INET6) { + rp = ipv4v6bindall; /* Take first IPV6 address available */ + break; + } + ipv4v6bindall = ipv4v6bindall->ai_next; /* Get next address info, if any */ + } + } + + for (/*rp = result*/; rp != NULL; rp = rp->ai_next) { + server_sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if (server_sock == -1) { + continue; + } + + if (rp->ai_family == AF_INET6) { + int ipv6only = host ? 1 : 0; + setsockopt(server_sock, IPPROTO_IPV6, IPV6_V6ONLY, &ipv6only, sizeof(ipv6only)); + } + + int opt = 1; + setsockopt(server_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); +#ifdef SO_NOSIGPIPE + set_nosigpipe(server_sock); +#endif + int err = set_reuseport(server_sock); + if (err == 0) { + LOGI("udp port reuse enabled"); + } +#ifdef IP_TOS + // Set QoS flag + int tos = 46; + setsockopt(server_sock, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)); +#endif + +#ifdef MODULE_REDIR + if (setsockopt(server_sock, SOL_IP, IP_TRANSPARENT, &opt, sizeof(opt))) { + ERROR("[udp] setsockopt IP_TRANSPARENT"); + exit(EXIT_FAILURE); + } + if (setsockopt(server_sock, IPPROTO_IP, IP_RECVORIGDSTADDR, &opt, sizeof(opt))) { + FATAL("[udp] setsockopt IP_RECVORIGDSTADDR"); + } +#endif + + s = bind(server_sock, rp->ai_addr, rp->ai_addrlen); + if (s == 0) { + /* We managed to bind successfully! */ + break; + } else { + ERROR("[udp] bind"); + } + + close(server_sock); + } + + if (rp == NULL) { + LOGE("[udp] cannot bind"); + return -1; + } + + freeaddrinfo(result); + + return server_sock; +} + +remote_ctx_t * +new_remote(int fd, server_ctx_t *server_ctx) +{ + remote_ctx_t *ctx = ss_malloc(sizeof(remote_ctx_t)); + memset(ctx, 0, sizeof(remote_ctx_t)); + + ctx->fd = fd; + ctx->server_ctx = server_ctx; + + ev_io_init(&ctx->io, remote_recv_cb, fd, EV_READ); + ev_timer_init(&ctx->watcher, remote_timeout_cb, server_ctx->timeout, + server_ctx->timeout); + + return ctx; +} + +server_ctx_t * +new_server_ctx(int fd) +{ + server_ctx_t *ctx = ss_malloc(sizeof(server_ctx_t)); + memset(ctx, 0, sizeof(server_ctx_t)); + + ctx->fd = fd; + + ev_io_init(&ctx->io, server_recv_cb, fd, EV_READ); + + return ctx; +} + +#ifdef MODULE_REMOTE +struct query_ctx * +new_query_ctx(char *buf, size_t len) +{ + struct query_ctx *ctx = ss_malloc(sizeof(struct query_ctx)); + memset(ctx, 0, sizeof(struct query_ctx)); + ctx->buf = ss_malloc(sizeof(buffer_t)); + balloc(ctx->buf, len); + memcpy(ctx->buf->array, buf, len); + ctx->buf->len = len; + return ctx; +} + +void +close_and_free_query(EV_P_ struct query_ctx *ctx) +{ + if (ctx != NULL) { + if (ctx->query != NULL) { + resolv_cancel(ctx->query); + ctx->query = NULL; + } + if (ctx->buf != NULL) { + bfree(ctx->buf); + ss_free(ctx->buf); + } + ss_free(ctx); + } +} + +#endif + +void +close_and_free_remote(EV_P_ remote_ctx_t *ctx) +{ + if (ctx != NULL) { + ev_timer_stop(EV_A_ & ctx->watcher); + ev_io_stop(EV_A_ & ctx->io); + close(ctx->fd); + ss_free(ctx); + } +} + +static void +remote_timeout_cb(EV_P_ ev_timer *watcher, int revents) +{ + remote_ctx_t *remote_ctx + = cork_container_of(watcher, remote_ctx_t, watcher); + + if (verbose) { + LOGI("[udp] connection timeout"); + } + + char *key = hash_key(remote_ctx->af, &remote_ctx->src_addr); + cache_remove(remote_ctx->server_ctx->conn_cache, key, HASH_KEY_LEN); +} + +#ifdef MODULE_REMOTE +static void +query_resolve_cb(struct sockaddr *addr, void *data) +{ + struct query_ctx *query_ctx = (struct query_ctx *)data; + struct ev_loop *loop = query_ctx->server_ctx->loop; + + if (verbose) { + LOGI("[udp] udns resolved"); + } + + query_ctx->query = NULL; + + if (addr == NULL) { + LOGE("[udp] udns returned an error"); + } else { + remote_ctx_t *remote_ctx = query_ctx->remote_ctx; + int cache_hit = 0; + + // Lookup in the conn cache + if (remote_ctx == NULL) { + char *key = hash_key(AF_UNSPEC, &query_ctx->src_addr); + cache_lookup(query_ctx->server_ctx->conn_cache, key, HASH_KEY_LEN, (void *)&remote_ctx); + } + + if (remote_ctx == NULL) { + int remotefd = create_remote_socket(addr->sa_family == AF_INET6); + if (remotefd != -1) { + setnonblocking(remotefd); +#ifdef SO_BROADCAST + set_broadcast(remotefd); +#endif +#ifdef SO_NOSIGPIPE + set_nosigpipe(remotefd); +#endif +#ifdef IP_TOS + // Set QoS flag + int tos = 46; + setsockopt(remotefd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)); +#endif +#ifdef SET_INTERFACE + if (query_ctx->server_ctx->iface) { + if (setinterface(remotefd, query_ctx->server_ctx->iface) == -1) + ERROR("setinterface"); + } +#endif + remote_ctx = new_remote(remotefd, query_ctx->server_ctx); + remote_ctx->src_addr = query_ctx->src_addr; + remote_ctx->server_ctx = query_ctx->server_ctx; + remote_ctx->addr_header_len = query_ctx->addr_header_len; + memcpy(remote_ctx->addr_header, query_ctx->addr_header, + query_ctx->addr_header_len); + } else { + ERROR("[udp] bind() error"); + } + } else { + cache_hit = 1; + } + + if (remote_ctx != NULL) { + memcpy(&remote_ctx->dst_addr, addr, sizeof(struct sockaddr_storage)); + + size_t addr_len = get_sockaddr_len(addr); + int s = sendto(remote_ctx->fd, query_ctx->buf->array, query_ctx->buf->len, + 0, addr, addr_len); + + if (s == -1) { + ERROR("[udp] sendto_remote"); + if (!cache_hit) { + close_and_free_remote(EV_A_ remote_ctx); + } + } else { + if (!cache_hit) { + // Add to conn cache + char *key = hash_key(AF_UNSPEC, &remote_ctx->src_addr); + cache_insert(query_ctx->server_ctx->conn_cache, key, HASH_KEY_LEN, (void *)remote_ctx); + ev_io_start(EV_A_ & remote_ctx->io); + ev_timer_start(EV_A_ & remote_ctx->watcher); + } + } + } + } + + // clean up + close_and_free_query(EV_A_ query_ctx); +} + +#endif + +static void +remote_recv_cb(EV_P_ ev_io *w, int revents) +{ + ssize_t r; + remote_ctx_t *remote_ctx = (remote_ctx_t *)w; + server_ctx_t *server_ctx = remote_ctx->server_ctx; + + // server has been closed + if (server_ctx == NULL) { + LOGE("[udp] invalid server"); + close_and_free_remote(EV_A_ remote_ctx); + return; + } + + struct sockaddr_storage src_addr; + socklen_t src_addr_len = sizeof(struct sockaddr_storage); + memset(&src_addr, 0, src_addr_len); + + buffer_t *buf = ss_malloc(sizeof(buffer_t)); + balloc(buf, buf_size); + + // recv + r = recvfrom(remote_ctx->fd, buf->array, buf_size, 0, (struct sockaddr *)&src_addr, &src_addr_len); + + if (r == -1) { + // error on recv + // simply drop that packet + ERROR("[udp] remote_recv_recvfrom"); + goto CLEAN_UP; + } else if (r > packet_size) { + LOGE("[udp] remote_recv_recvfrom fragmentation"); + goto CLEAN_UP; + } + + buf->len = r; + +#ifdef MODULE_LOCAL + int err = ss_decrypt_all(buf, server_ctx->method, 0, buf_size); + if (err) { + // drop the packet silently + goto CLEAN_UP; + } + + //SSR beg + if (server_ctx->protocol_plugin) { + obfs_class *protocol_plugin = server_ctx->protocol_plugin; + if (protocol_plugin->client_udp_post_decrypt) { + buf->len = protocol_plugin->client_udp_post_decrypt(server_ctx->protocol, &buf->array, buf->len, &buf->capacity); + if ((int)buf->len < 0) { + LOGE("client_udp_post_decrypt"); + close_and_free_remote(EV_A_ remote_ctx); + return; + } + if ( buf->len == 0 ) + return; + } + } + // SSR end + +#ifdef MODULE_REDIR + struct sockaddr_storage dst_addr; + memset(&dst_addr, 0, sizeof(struct sockaddr_storage)); + int len = parse_udprealy_header(buf->array, buf->len, NULL, NULL, &dst_addr); + + if (dst_addr.ss_family != AF_INET && dst_addr.ss_family != AF_INET6) { + LOGI("[udp] ss-redir does not support domain name"); + goto CLEAN_UP; + } + + if (verbose) { + char src[SS_ADDRSTRLEN]; + char dst[SS_ADDRSTRLEN]; + strcpy(src, get_addr_str((struct sockaddr *)&src_addr)); + strcpy(dst, get_addr_str((struct sockaddr *)&dst_addr)); + LOGI("[udp] recv %s via %s", dst, src); + } +#else + int len = parse_udprealy_header(buf->array, buf->len, NULL, NULL, NULL); +#endif + + if (len == 0) { + LOGI("[udp] error in parse header"); + // error in parse header + goto CLEAN_UP; + } + + // server may return using a different address type other than the type we + // have used during sending +#if defined(MODULE_TUNNEL) || defined(MODULE_REDIR) + // Construct packet + buf->len -= len; + memmove(buf->array, buf->array + len, buf->len); +#else +#ifdef ANDROID + rx += buf->len; +#endif + // Construct packet + brealloc(buf, buf->len + 3, buf_size); + memmove(buf->array + 3, buf->array, buf->len); + memset(buf->array, 0, 3); + buf->len += 3; +#endif + +#endif + +#ifdef MODULE_REMOTE + + rx += buf->len; + + char addr_header_buf[512]; + char *addr_header = remote_ctx->addr_header; + int addr_header_len = remote_ctx->addr_header_len; + + if (remote_ctx->af == AF_INET || remote_ctx->af == AF_INET6) { + addr_header_len = construct_udprealy_header(&src_addr, addr_header_buf); + addr_header = addr_header_buf; + } + + // Construct packet + brealloc(buf, buf->len + addr_header_len, buf_size); + memmove(buf->array + addr_header_len, buf->array, buf->len); + memcpy(buf->array, addr_header, addr_header_len); + buf->len += addr_header_len; + + int err = ss_encrypt_all(buf, server_ctx->method, 0, buf_size); + if (err) { + // drop the packet silently + goto CLEAN_UP; + } + +#endif + + if (buf->len > packet_size) { + LOGE("[udp] remote_recv_sendto fragmentation"); + goto CLEAN_UP; + } + + size_t remote_src_addr_len = get_sockaddr_len((struct sockaddr *)&remote_ctx->src_addr); + +#ifdef MODULE_REDIR + + size_t remote_dst_addr_len = get_sockaddr_len((struct sockaddr *)&dst_addr); + + int src_fd = socket(remote_ctx->src_addr.ss_family, SOCK_DGRAM, 0); + if (src_fd < 0) { + ERROR("[udp] remote_recv_socket"); + goto CLEAN_UP; + } + int opt = 1; + if (setsockopt(src_fd, SOL_IP, IP_TRANSPARENT, &opt, sizeof(opt))) { + ERROR("[udp] remote_recv_setsockopt"); + close(src_fd); + goto CLEAN_UP; + } + if (setsockopt(src_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) { + ERROR("[udp] remote_recv_setsockopt"); + close(src_fd); + goto CLEAN_UP; + } +#ifdef IP_TOS + // Set QoS flag + int tos = 46; + setsockopt(src_fd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)); +#endif + if (bind(src_fd, (struct sockaddr *)&dst_addr, remote_dst_addr_len) != 0) { + ERROR("[udp] remote_recv_bind"); + close(src_fd); + goto CLEAN_UP; + } + + int s = sendto(src_fd, buf->array, buf->len, 0, + (struct sockaddr *)&remote_ctx->src_addr, remote_src_addr_len); + if (s == -1) { + ERROR("[udp] remote_recv_sendto"); + close(src_fd); + goto CLEAN_UP; + } + close(src_fd); + +#else + + int s = sendto(server_ctx->fd, buf->array, buf->len, 0, + (struct sockaddr *)&remote_ctx->src_addr, remote_src_addr_len); + if (s == -1) { + ERROR("[udp] remote_recv_sendto"); + goto CLEAN_UP; + } + +#endif + + // handle the UDP packet successfully, + // triger the timer + ev_timer_again(EV_A_ & remote_ctx->watcher); + +CLEAN_UP: + + bfree(buf); + ss_free(buf); +} + +static void +server_recv_cb(EV_P_ ev_io *w, int revents) +{ + server_ctx_t *server_ctx = (server_ctx_t *)w; + struct sockaddr_storage src_addr; + memset(&src_addr, 0, sizeof(struct sockaddr_storage)); + + buffer_t *buf = ss_malloc(sizeof(buffer_t)); + balloc(buf, buf_size); + + socklen_t src_addr_len = sizeof(struct sockaddr_storage); + unsigned int offset = 0; + +#ifdef MODULE_REDIR + char control_buffer[64] = { 0 }; + struct msghdr msg; + memset(&msg, 0, sizeof(struct msghdr)); + struct iovec iov[1]; + struct sockaddr_storage dst_addr; + memset(&dst_addr, 0, sizeof(struct sockaddr_storage)); + + msg.msg_name = &src_addr; + msg.msg_namelen = src_addr_len; + msg.msg_control = control_buffer; + msg.msg_controllen = sizeof(control_buffer); + + iov[0].iov_base = buf->array; + iov[0].iov_len = buf_size; + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + buf->len = recvmsg(server_ctx->fd, &msg, 0); + if (buf->len == -1) { + ERROR("[udp] server_recvmsg"); + goto CLEAN_UP; + } else if (buf->len > packet_size) { + ERROR("[udp] UDP server_recv_recvmsg fragmentation"); + goto CLEAN_UP; + } + + if (get_dstaddr(&msg, &dst_addr)) { + LOGE("[udp] unable to get dest addr"); + goto CLEAN_UP; + } + + src_addr_len = msg.msg_namelen; +#else + ssize_t r; + r = recvfrom(server_ctx->fd, buf->array, buf_size, + 0, (struct sockaddr *)&src_addr, &src_addr_len); + + if (r == -1) { + // error on recv + // simply drop that packet + ERROR("[udp] server_recv_recvfrom"); + goto CLEAN_UP; + } else if (r > packet_size) { + ERROR("[udp] server_recv_recvfrom fragmentation"); + goto CLEAN_UP; + } + + buf->len = r; +#endif + +#ifdef MODULE_REMOTE + tx += buf->len; + + int err = ss_decrypt_all(buf, server_ctx->method, server_ctx->auth, buf_size); + if (err) { + // drop the packet silently + goto CLEAN_UP; + } +#endif + +#ifdef MODULE_LOCAL +#if !defined(MODULE_TUNNEL) && !defined(MODULE_REDIR) +#ifdef ANDROID + tx += buf->len; +#endif + uint8_t frag = *(uint8_t *)(buf->array + 2); + offset += 3; +#endif +#endif + + /* + * + * 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 | HMAC-SHA1 | + * +------+----------+----------+----------+-------------+ + * | 1 | Variable | 2 | Variable | 10 | + * +------+----------+----------+----------+-------------+ + * + * If ATYP & ONETIMEAUTH_FLAG(0x10) != 0, Authentication (HMAC-SHA1) is enabled. + * + * The key of HMAC-SHA1 is (IV + KEY) and the input is the whole packet. + * The output of HMAC-SHA is truncated to 10 bytes (leftmost bits). + * + * 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 | + * +-------+--------------+ + * + */ + +#ifdef MODULE_REDIR + if (verbose) { + char src[SS_ADDRSTRLEN]; + char dst[SS_ADDRSTRLEN]; + strcpy(src, get_addr_str((struct sockaddr *)&src_addr)); + strcpy(dst, get_addr_str((struct sockaddr *)&dst_addr)); + LOGI("[udp] redir to %s from %s", dst, src); + } + + char addr_header[512] = { 0 }; + int addr_header_len = construct_udprealy_header(&dst_addr, addr_header); + + if (addr_header_len == 0) { + LOGE("[udp] failed to parse tproxy addr"); + goto CLEAN_UP; + } + + // reconstruct the buffer + brealloc(buf, buf->len + addr_header_len, buf_size); + memmove(buf->array + addr_header_len, buf->array, buf->len); + memcpy(buf->array, addr_header, addr_header_len); + buf->len += addr_header_len; + +#elif MODULE_TUNNEL + + char addr_header[512] = { 0 }; + char *host = server_ctx->tunnel_addr.host; + char *port = server_ctx->tunnel_addr.port; + uint16_t port_num = (uint16_t)atoi(port); + uint16_t port_net_num = htons(port_num); + int addr_header_len = 0; + + struct cork_ip ip; + if (cork_ip_init(&ip, host) != -1) { + if (ip.version == 4) { + // send as IPv4 + struct in_addr host_addr; + memset(&host_addr, 0, sizeof(struct in_addr)); + int host_len = sizeof(struct in_addr); + + if (dns_pton(AF_INET, host, &host_addr) == -1) { + FATAL("IP parser error"); + } + addr_header[addr_header_len++] = 1; + memcpy(addr_header + addr_header_len, &host_addr, host_len); + addr_header_len += host_len; + } else if (ip.version == 6) { + // send as IPv6 + struct in6_addr host_addr; + memset(&host_addr, 0, sizeof(struct in6_addr)); + int host_len = sizeof(struct in6_addr); + + if (dns_pton(AF_INET6, host, &host_addr) == -1) { + FATAL("IP parser error"); + } + addr_header[addr_header_len++] = 4; + memcpy(addr_header + addr_header_len, &host_addr, host_len); + addr_header_len += host_len; + } else { + FATAL("IP parser error"); + } + } else { + // send as domain + int host_len = strlen(host); + + addr_header[addr_header_len++] = 3; + addr_header[addr_header_len++] = host_len; + memcpy(addr_header + addr_header_len, host, host_len); + addr_header_len += host_len; + } + memcpy(addr_header + addr_header_len, &port_net_num, 2); + addr_header_len += 2; + + // reconstruct the buffer + brealloc(buf, buf->len + addr_header_len, buf_size); + memmove(buf->array + addr_header_len, buf->array, buf->len); + memcpy(buf->array, addr_header, addr_header_len); + buf->len += addr_header_len; + +#else + + char host[257] = { 0 }; + char port[64] = { 0 }; + struct sockaddr_storage dst_addr; + memset(&dst_addr, 0, sizeof(struct sockaddr_storage)); + + int addr_header_len = parse_udprealy_header(buf->array + offset, buf->len - offset, + host, port, &dst_addr); + if (addr_header_len == 0) { + // error in parse header + goto CLEAN_UP; + } + + char *addr_header = buf->array + offset; +#endif + +#ifdef MODULE_LOCAL + char *key = hash_key(server_ctx->remote_addr->sa_family, &src_addr); +#else + char *key = hash_key(dst_addr.ss_family, &src_addr); +#endif + + struct cache *conn_cache = server_ctx->conn_cache; + + remote_ctx_t *remote_ctx = NULL; + cache_lookup(conn_cache, key, HASH_KEY_LEN, (void *)&remote_ctx); + + if (remote_ctx != NULL) { + if (sockaddr_cmp(&src_addr, &remote_ctx->src_addr, sizeof(src_addr))) { + remote_ctx = NULL; + } + } + + // reset the timer + if (remote_ctx != NULL) { + ev_timer_again(EV_A_ & remote_ctx->watcher); + } + + if (remote_ctx == NULL) { + if (verbose) { +#ifdef MODULE_REDIR + char src[SS_ADDRSTRLEN]; + char dst[SS_ADDRSTRLEN]; + strcpy(src, get_addr_str((struct sockaddr *)&src_addr)); + strcpy(dst, get_addr_str((struct sockaddr *)&dst_addr)); + LOGI("[udp] cache miss: %s <-> %s", dst, src); +#else + LOGI("[udp] cache miss: %s:%s <-> %s", host, port, + get_addr_str((struct sockaddr *)&src_addr)); +#endif + } + } else { + if (verbose) { +#ifdef MODULE_REDIR + char src[SS_ADDRSTRLEN]; + char dst[SS_ADDRSTRLEN]; + strcpy(src, get_addr_str((struct sockaddr *)&src_addr)); + strcpy(dst, get_addr_str((struct sockaddr *)&dst_addr)); + LOGI("[udp] cache hit: %s <-> %s", dst, src); +#else + LOGI("[udp] cache hit: %s:%s <-> %s", host, port, + get_addr_str((struct sockaddr *)&src_addr)); +#endif + } + } + +#ifdef MODULE_LOCAL + +#if !defined(MODULE_TUNNEL) && !defined(MODULE_REDIR) + if (frag) { + LOGE("[udp] drop a message since frag is not 0, but %d", frag); + goto CLEAN_UP; + } +#endif + + const struct sockaddr *remote_addr = server_ctx->remote_addr; + const int remote_addr_len = server_ctx->remote_addr_len; + + if (remote_ctx == NULL) { + // Bind to any port + int remotefd = create_remote_socket(remote_addr->sa_family == AF_INET6); + if (remotefd < 0) { + ERROR("[udp] udprelay bind() error"); + goto CLEAN_UP; + } + setnonblocking(remotefd); + +#ifdef SO_NOSIGPIPE + set_nosigpipe(remotefd); +#endif +#ifdef IP_TOS + // Set QoS flag + int tos = 46; + setsockopt(remotefd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)); +#endif +#ifdef SET_INTERFACE + if (server_ctx->iface) { + if (setinterface(remotefd, server_ctx->iface) == -1) + ERROR("setinterface"); + } +#endif + +#ifdef ANDROID + if (vpn) { + if (protect_socket(remotefd) == -1) { + ERROR("protect_socket"); + close(remotefd); + goto CLEAN_UP; + } + } +#endif + + // Init remote_ctx + remote_ctx = new_remote(remotefd, server_ctx); + remote_ctx->src_addr = src_addr; + remote_ctx->af = remote_addr->sa_family; + remote_ctx->addr_header_len = addr_header_len; + memcpy(remote_ctx->addr_header, addr_header, addr_header_len); + + // Add to conn cache + cache_insert(conn_cache, key, HASH_KEY_LEN, (void *)remote_ctx); + + // Start remote io + ev_io_start(EV_A_ & remote_ctx->io); + ev_timer_start(EV_A_ & remote_ctx->watcher); + } + + if (offset > 0) { + buf->len -= offset; + memmove(buf->array, buf->array + offset, buf->len); + } + + if (server_ctx->auth) { + buf->array[0] |= ONETIMEAUTH_FLAG; + } + + // SSR beg + if (server_ctx->protocol_plugin) { + obfs_class *protocol_plugin = server_ctx->protocol_plugin; + if (protocol_plugin->client_udp_pre_encrypt) { + buf->len = protocol_plugin->client_udp_pre_encrypt(server_ctx->protocol, &buf->array, buf->len, &buf->capacity); + } + } + //SSR end + + int err = ss_encrypt_all(buf, server_ctx->method, server_ctx->auth, buf->len); + + if (err) { + // drop the packet silently + goto CLEAN_UP; + } + + if (buf->len > packet_size) { + LOGE("[udp] server_recv_sendto fragmentation"); + goto CLEAN_UP; + } + + int s = sendto(remote_ctx->fd, buf->array, buf->len, 0, remote_addr, remote_addr_len); + + if (s == -1) { + ERROR("[udp] server_recv_sendto"); + } + +#else + + int cache_hit = 0; + int need_query = 0; + + if (buf->len - addr_header_len > packet_size) { + LOGE("[udp] server_recv_sendto fragmentation"); + goto CLEAN_UP; + } + + if (remote_ctx != NULL) { + cache_hit = 1; + // detect destination mismatch + if (remote_ctx->addr_header_len != addr_header_len + || memcmp(addr_header, remote_ctx->addr_header, addr_header_len) != 0) { + if (dst_addr.ss_family != AF_INET && dst_addr.ss_family != AF_INET6) { + need_query = 1; + } + } else { + memcpy(&dst_addr, &remote_ctx->dst_addr, sizeof(struct sockaddr_storage)); + } + } else { + if (dst_addr.ss_family == AF_INET || dst_addr.ss_family == AF_INET6) { + int remotefd = create_remote_socket(dst_addr.ss_family == AF_INET6); + if (remotefd != -1) { + setnonblocking(remotefd); +#ifdef SO_BROADCAST + set_broadcast(remotefd); +#endif +#ifdef SO_NOSIGPIPE + set_nosigpipe(remotefd); +#endif +#ifdef IP_TOS + // Set QoS flag + int tos = 46; + setsockopt(remotefd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)); +#endif +#ifdef SET_INTERFACE + if (server_ctx->iface) { + if (setinterface(remotefd, server_ctx->iface) == -1) + ERROR("setinterface"); + } +#endif + remote_ctx = new_remote(remotefd, server_ctx); + remote_ctx->src_addr = src_addr; + remote_ctx->server_ctx = server_ctx; + remote_ctx->addr_header_len = addr_header_len; + memcpy(remote_ctx->addr_header, addr_header, addr_header_len); + memcpy(&remote_ctx->dst_addr, &dst_addr, sizeof(struct sockaddr_storage)); + } else { + ERROR("[udp] bind() error"); + goto CLEAN_UP; + } + } + } + + if (remote_ctx != NULL && !need_query) { + size_t addr_len = get_sockaddr_len((struct sockaddr *)&dst_addr); + int s = sendto(remote_ctx->fd, buf->array + addr_header_len, + buf->len - addr_header_len, 0, + (struct sockaddr *)&dst_addr, addr_len); + + if (s == -1) { + ERROR("[udp] sendto_remote"); + if (!cache_hit) { + close_and_free_remote(EV_A_ remote_ctx); + } + } else { + if (!cache_hit) { + // Add to conn cache + remote_ctx->af = dst_addr.ss_family; + char *key = hash_key(remote_ctx->af, &remote_ctx->src_addr); + cache_insert(server_ctx->conn_cache, key, HASH_KEY_LEN, (void *)remote_ctx); + + ev_io_start(EV_A_ & remote_ctx->io); + ev_timer_start(EV_A_ & remote_ctx->watcher); + } + } + } else { + struct addrinfo hints; + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_protocol = IPPROTO_UDP; + + struct query_ctx *query_ctx = new_query_ctx(buf->array + addr_header_len, + buf->len - addr_header_len); + query_ctx->server_ctx = server_ctx; + query_ctx->addr_header_len = addr_header_len; + query_ctx->src_addr = src_addr; + memcpy(query_ctx->addr_header, addr_header, addr_header_len); + + if (need_query) { + query_ctx->remote_ctx = remote_ctx; + } + + struct ResolvQuery *query = resolv_query(host, query_resolve_cb, + NULL, query_ctx, htons(atoi(port))); + if (query == NULL) { + ERROR("[udp] unable to create DNS query"); + close_and_free_query(EV_A_ query_ctx); + goto CLEAN_UP; + } + query_ctx->query = query; + } +#endif + +CLEAN_UP: + bfree(buf); + ss_free(buf); +} + +void +free_cb(void *key, void *element) +{ + remote_ctx_t *remote_ctx = (remote_ctx_t *)element; + + if (verbose) { + LOGI("[udp] one connection freed"); + } + + close_and_free_remote(EV_DEFAULT, remote_ctx); +} + +int +init_udprelay(const char *server_host, const char *server_port, +#ifdef MODULE_LOCAL + const struct sockaddr *remote_addr, const int remote_addr_len, +#ifdef MODULE_TUNNEL + const ss_addr_t tunnel_addr, +#endif +#endif + int mtu, int method, int auth, int timeout, const char *iface, const char *protocol, const char *protocol_param) +{ + // Initialize ev loop + struct ev_loop *loop = EV_DEFAULT; + + // Initialize MTU + if (mtu > 0) { + packet_size = mtu - 1 - 28 - 2 - 64; + buf_size = packet_size * 2; + } + + // Initialize cache + struct cache *conn_cache; + cache_create(&conn_cache, MAX_UDP_CONN_NUM, free_cb); + + // //////////////////////////////////////////////// + // Setup server context + + // Bind to port + int serverfd = create_server_socket(server_host, server_port); + if (serverfd < 0) { + FATAL("[udp] bind() error"); + } + setnonblocking(serverfd); + if (protocol != NULL && strcmp(protocol, "verify_sha1") == 0) { + auth = 1; + protocol = NULL; + } + + server_ctx_t *server_ctx = new_server_ctx(serverfd); +#ifdef MODULE_REMOTE + server_ctx->loop = loop; +#endif + server_ctx->auth = auth; + server_ctx->timeout = max(timeout, MIN_UDP_TIMEOUT); + server_ctx->method = method; + server_ctx->iface = iface; + server_ctx->conn_cache = conn_cache; +#ifdef MODULE_LOCAL + server_ctx->remote_addr = remote_addr; + server_ctx->remote_addr_len = remote_addr_len; + //SSR beg + server_ctx->protocol_plugin = new_obfs_class((char *)protocol); + if (server_ctx->protocol_plugin) { + server_ctx->protocol = server_ctx->protocol_plugin->new_obfs(); + server_ctx->protocol_global = server_ctx->protocol_plugin->init_data(); + } + + server_info _server_info; + memset(&_server_info, 0, sizeof(server_info)); + strcpy(_server_info.host, inet_ntoa(((struct sockaddr_in*)remote_addr)->sin_addr)); + _server_info.port = ((struct sockaddr_in*)remote_addr)->sin_port; + _server_info.port = _server_info.port >> 8 | _server_info.port << 8; + _server_info.g_data = server_ctx->protocol_global; + _server_info.param = (char *)protocol_param; + _server_info.key = enc_get_key(); + _server_info.key_len = enc_get_key_len(); + + if (server_ctx->protocol_plugin) + server_ctx->protocol_plugin->set_server_info(server_ctx->protocol, &_server_info); + //SSR end +#ifdef MODULE_TUNNEL + server_ctx->tunnel_addr = tunnel_addr; +#endif +#endif + + ev_io_start(loop, &server_ctx->io); + + server_ctx_list[server_num++] = server_ctx; + + return 0; +} + +void +free_udprelay() +{ + struct ev_loop *loop = EV_DEFAULT; + while (server_num-- > 0) { + server_ctx_t *server_ctx = server_ctx_list[server_num]; + +#ifdef MODULE_LOCAL + //SSR beg + if (server_ctx->protocol_plugin) { + server_ctx->protocol_plugin->dispose(server_ctx->protocol); + server_ctx->protocol = NULL; + free_obfs_class(server_ctx->protocol_plugin); + server_ctx->protocol_plugin = NULL; + } + //SSR end +#endif + + ev_io_stop(loop, &server_ctx->io); + close(server_ctx->fd); + cache_delete(server_ctx->conn_cache, 0); + ss_free(server_ctx); + server_ctx_list[server_num] = NULL; + } +} diff --git a/shadowsocksr-libev/src/server/udprelay.h b/shadowsocksr-libev/src/server/udprelay.h new file mode 100644 index 000000000..89876d4f4 --- /dev/null +++ b/shadowsocksr-libev/src/server/udprelay.h @@ -0,0 +1,95 @@ +/* + * udprelay.h - Define UDP relay's buffers and callbacks + * + * Copyright (C) 2013 - 2016, Max Lv + * + * This file is part of the shadowsocks-libev. + * + * shadowsocks-libev is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * shadowsocks-libev 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with shadowsocks-libev; see the file COPYING. If not, see + * . + */ + +#ifndef _UDPRELAY_H +#define _UDPRELAY_H + +#include +#include + +#include "encrypt.h" +#include "jconf.h" +#include "obfs.h" + +#ifdef MODULE_REMOTE +#include "resolv.h" +#endif + +#include "cache.h" + +#include "common.h" + +#define MAX_UDP_PACKET_SIZE (65507) + +#define DEFAULT_PACKET_SIZE 1397 // 1492 - 1 - 28 - 2 - 64 = 1397, the default MTU for UDP relay + +typedef struct server_ctx { + ev_io io; + int fd; + int method; + int auth; + int timeout; + const char *iface; + struct cache *conn_cache; +#ifdef MODULE_LOCAL + const struct sockaddr *remote_addr; + int remote_addr_len; +#ifdef MODULE_TUNNEL + ss_addr_t tunnel_addr; +#endif +#endif +#ifdef MODULE_REMOTE + struct ev_loop *loop; +#endif + // SSR + obfs *protocol; + obfs_class *protocol_plugin; + void *protocol_global; +} server_ctx_t; + +#ifdef MODULE_REMOTE +typedef struct query_ctx { + struct ResolvQuery *query; + struct sockaddr_storage src_addr; + buffer_t *buf; + int addr_header_len; + char addr_header[384]; + struct server_ctx *server_ctx; + struct remote_ctx *remote_ctx; +} query_ctx_t; +#endif + +typedef struct remote_ctx { + ev_io io; + ev_timer watcher; + int af; + int fd; + int addr_header_len; + char addr_header[384]; + struct sockaddr_storage src_addr; +#ifdef MODULE_REMOTE + struct sockaddr_storage dst_addr; +#endif + struct server_ctx *server_ctx; +} remote_ctx_t; + +#endif // _UDPRELAY_H diff --git a/shadowsocksr-libev/src/server/uthash.h b/shadowsocksr-libev/src/server/uthash.h new file mode 100644 index 000000000..45d1f9fc1 --- /dev/null +++ b/shadowsocksr-libev/src/server/uthash.h @@ -0,0 +1,1074 @@ +/* +Copyright (c) 2003-2016, Troy D. Hanson http://troydhanson.github.com/uthash/ +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef UTHASH_H +#define UTHASH_H + +#define UTHASH_VERSION 2.0.1 + +#include /* memcmp,strlen */ +#include /* ptrdiff_t */ +#include /* exit() */ + +/* These macros use decltype or the earlier __typeof GNU extension. + As decltype is only available in newer compilers (VS2010 or gcc 4.3+ + when compiling c++ source) this code uses whatever method is needed + or, for VS2008 where neither is available, uses casting workarounds. */ +#if defined(_MSC_VER) /* MS compiler */ +#if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */ +#define DECLTYPE(x) (decltype(x)) +#else /* VS2008 or older (or VS2010 in C mode) */ +#define NO_DECLTYPE +#define DECLTYPE(x) +#endif +#elif defined(__BORLANDC__) || defined(__LCC__) || defined(__WATCOMC__) +#define NO_DECLTYPE +#define DECLTYPE(x) +#else /* GNU, Sun and other compilers */ +#define DECLTYPE(x) (__typeof(x)) +#endif + +#ifdef NO_DECLTYPE +#define DECLTYPE_ASSIGN(dst,src) \ +do { \ + char **_da_dst = (char**)(&(dst)); \ + *_da_dst = (char*)(src); \ +} while (0) +#else +#define DECLTYPE_ASSIGN(dst,src) \ +do { \ + (dst) = DECLTYPE(dst)(src); \ +} while (0) +#endif + +/* a number of the hash function use uint32_t which isn't defined on Pre VS2010 */ +#if defined(_WIN32) +#if defined(_MSC_VER) && _MSC_VER >= 1600 +#include +#elif defined(__WATCOMC__) || defined(__MINGW32__) || defined(__CYGWIN__) +#include +#else +typedef unsigned int uint32_t; +typedef unsigned char uint8_t; +#endif +#elif defined(__GNUC__) && !defined(__VXWORKS__) +#include +#else +typedef unsigned int uint32_t; +typedef unsigned char uint8_t; +#endif + +#ifndef uthash_fatal +#define uthash_fatal(msg) exit(-1) /* fatal error (out of memory,etc) */ +#endif +#ifndef uthash_malloc +#define uthash_malloc(sz) malloc(sz) /* malloc fcn */ +#endif +#ifndef uthash_free +#define uthash_free(ptr,sz) free(ptr) /* free fcn */ +#endif +#ifndef uthash_strlen +#define uthash_strlen(s) strlen(s) +#endif +#ifndef uthash_memcmp +#define uthash_memcmp(a,b,n) memcmp(a,b,n) +#endif + +#ifndef uthash_noexpand_fyi +#define uthash_noexpand_fyi(tbl) /* can be defined to log noexpand */ +#endif +#ifndef uthash_expand_fyi +#define uthash_expand_fyi(tbl) /* can be defined to log expands */ +#endif + +/* initial number of buckets */ +#define HASH_INITIAL_NUM_BUCKETS 32U /* initial number of buckets */ +#define HASH_INITIAL_NUM_BUCKETS_LOG2 5U /* lg2 of initial number of buckets */ +#define HASH_BKT_CAPACITY_THRESH 10U /* expand when bucket count reaches */ + +/* calculate the element whose hash handle address is hhp */ +#define ELMT_FROM_HH(tbl,hhp) ((void*)(((char*)(hhp)) - ((tbl)->hho))) +/* calculate the hash handle from element address elp */ +#define HH_FROM_ELMT(tbl,elp) ((UT_hash_handle *)(((char*)(elp)) + ((tbl)->hho))) + +#define HASH_VALUE(keyptr,keylen,hashv) \ +do { \ + HASH_FCN(keyptr, keylen, hashv); \ +} while (0) + +#define HASH_FIND_BYHASHVALUE(hh,head,keyptr,keylen,hashval,out) \ +do { \ + (out) = NULL; \ + if (head) { \ + unsigned _hf_bkt; \ + HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _hf_bkt); \ + if (HASH_BLOOM_TEST((head)->hh.tbl, hashval) != 0) { \ + HASH_FIND_IN_BKT((head)->hh.tbl, hh, (head)->hh.tbl->buckets[ _hf_bkt ], keyptr, keylen, hashval, out); \ + } \ + } \ +} while (0) + +#define HASH_FIND(hh,head,keyptr,keylen,out) \ +do { \ + unsigned _hf_hashv; \ + HASH_VALUE(keyptr, keylen, _hf_hashv); \ + HASH_FIND_BYHASHVALUE(hh, head, keyptr, keylen, _hf_hashv, out); \ +} while (0) + +#ifdef HASH_BLOOM +#define HASH_BLOOM_BITLEN (1UL << HASH_BLOOM) +#define HASH_BLOOM_BYTELEN (HASH_BLOOM_BITLEN/8UL) + (((HASH_BLOOM_BITLEN%8UL)!=0UL) ? 1UL : 0UL) +#define HASH_BLOOM_MAKE(tbl) \ +do { \ + (tbl)->bloom_nbits = HASH_BLOOM; \ + (tbl)->bloom_bv = (uint8_t*)uthash_malloc(HASH_BLOOM_BYTELEN); \ + if (!((tbl)->bloom_bv)) { uthash_fatal( "out of memory"); } \ + memset((tbl)->bloom_bv, 0, HASH_BLOOM_BYTELEN); \ + (tbl)->bloom_sig = HASH_BLOOM_SIGNATURE; \ +} while (0) + +#define HASH_BLOOM_FREE(tbl) \ +do { \ + uthash_free((tbl)->bloom_bv, HASH_BLOOM_BYTELEN); \ +} while (0) + +#define HASH_BLOOM_BITSET(bv,idx) (bv[(idx)/8U] |= (1U << ((idx)%8U))) +#define HASH_BLOOM_BITTEST(bv,idx) (bv[(idx)/8U] & (1U << ((idx)%8U))) + +#define HASH_BLOOM_ADD(tbl,hashv) \ + HASH_BLOOM_BITSET((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1U))) + +#define HASH_BLOOM_TEST(tbl,hashv) \ + HASH_BLOOM_BITTEST((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1U))) + +#else +#define HASH_BLOOM_MAKE(tbl) +#define HASH_BLOOM_FREE(tbl) +#define HASH_BLOOM_ADD(tbl,hashv) +#define HASH_BLOOM_TEST(tbl,hashv) (1) +#define HASH_BLOOM_BYTELEN 0U +#endif + +#define HASH_MAKE_TABLE(hh,head) \ +do { \ + (head)->hh.tbl = (UT_hash_table*)uthash_malloc( \ + sizeof(UT_hash_table)); \ + if (!((head)->hh.tbl)) { uthash_fatal( "out of memory"); } \ + memset((head)->hh.tbl, 0, sizeof(UT_hash_table)); \ + (head)->hh.tbl->tail = &((head)->hh); \ + (head)->hh.tbl->num_buckets = HASH_INITIAL_NUM_BUCKETS; \ + (head)->hh.tbl->log2_num_buckets = HASH_INITIAL_NUM_BUCKETS_LOG2; \ + (head)->hh.tbl->hho = (char*)(&(head)->hh) - (char*)(head); \ + (head)->hh.tbl->buckets = (UT_hash_bucket*)uthash_malloc( \ + HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \ + if (! (head)->hh.tbl->buckets) { uthash_fatal( "out of memory"); } \ + memset((head)->hh.tbl->buckets, 0, \ + HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \ + HASH_BLOOM_MAKE((head)->hh.tbl); \ + (head)->hh.tbl->signature = HASH_SIGNATURE; \ +} while (0) + +#define HASH_REPLACE_BYHASHVALUE_INORDER(hh,head,fieldname,keylen_in,hashval,add,replaced,cmpfcn) \ +do { \ + (replaced) = NULL; \ + HASH_FIND_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, replaced); \ + if (replaced) { \ + HASH_DELETE(hh, head, replaced); \ + } \ + HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, &((add)->fieldname), keylen_in, hashval, add, cmpfcn); \ +} while (0) + +#define HASH_REPLACE_BYHASHVALUE(hh,head,fieldname,keylen_in,hashval,add,replaced) \ +do { \ + (replaced) = NULL; \ + HASH_FIND_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, replaced); \ + if (replaced) { \ + HASH_DELETE(hh, head, replaced); \ + } \ + HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, add); \ +} while (0) + +#define HASH_REPLACE(hh,head,fieldname,keylen_in,add,replaced) \ +do { \ + unsigned _hr_hashv; \ + HASH_VALUE(&((add)->fieldname), keylen_in, _hr_hashv); \ + HASH_REPLACE_BYHASHVALUE(hh, head, fieldname, keylen_in, _hr_hashv, add, replaced); \ +} while (0) + +#define HASH_REPLACE_INORDER(hh,head,fieldname,keylen_in,add,replaced,cmpfcn) \ +do { \ + unsigned _hr_hashv; \ + HASH_VALUE(&((add)->fieldname), keylen_in, _hr_hashv); \ + HASH_REPLACE_BYHASHVALUE_INORDER(hh, head, fieldname, keylen_in, _hr_hashv, add, replaced, cmpfcn); \ +} while (0) + +#define HASH_APPEND_LIST(hh, head, add) \ +do { \ + (add)->hh.next = NULL; \ + (add)->hh.prev = ELMT_FROM_HH((head)->hh.tbl, (head)->hh.tbl->tail); \ + (head)->hh.tbl->tail->next = (add); \ + (head)->hh.tbl->tail = &((add)->hh); \ +} while (0) + +#define HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh,head,keyptr,keylen_in,hashval,add,cmpfcn) \ +do { \ + unsigned _ha_bkt; \ + (add)->hh.hashv = (hashval); \ + (add)->hh.key = (char*) (keyptr); \ + (add)->hh.keylen = (unsigned) (keylen_in); \ + if (!(head)) { \ + (add)->hh.next = NULL; \ + (add)->hh.prev = NULL; \ + (head) = (add); \ + HASH_MAKE_TABLE(hh, head); \ + } else { \ + struct UT_hash_handle *_hs_iter = &(head)->hh; \ + (add)->hh.tbl = (head)->hh.tbl; \ + do { \ + if (cmpfcn(DECLTYPE(head) ELMT_FROM_HH((head)->hh.tbl, _hs_iter), add) > 0) \ + break; \ + } while ((_hs_iter = _hs_iter->next)); \ + if (_hs_iter) { \ + (add)->hh.next = _hs_iter; \ + if (((add)->hh.prev = _hs_iter->prev)) { \ + HH_FROM_ELMT((head)->hh.tbl, _hs_iter->prev)->next = (add); \ + } else { \ + (head) = (add); \ + } \ + _hs_iter->prev = (add); \ + } else { \ + HASH_APPEND_LIST(hh, head, add); \ + } \ + } \ + (head)->hh.tbl->num_items++; \ + HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _ha_bkt); \ + HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt], &(add)->hh); \ + HASH_BLOOM_ADD((head)->hh.tbl, hashval); \ + HASH_EMIT_KEY(hh, head, keyptr, keylen_in); \ + HASH_FSCK(hh, head); \ +} while (0) + +#define HASH_ADD_KEYPTR_INORDER(hh,head,keyptr,keylen_in,add,cmpfcn) \ +do { \ + unsigned _hs_hashv; \ + HASH_VALUE(keyptr, keylen_in, _hs_hashv); \ + HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, keyptr, keylen_in, _hs_hashv, add, cmpfcn); \ +} while (0) + +#define HASH_ADD_BYHASHVALUE_INORDER(hh,head,fieldname,keylen_in,hashval,add,cmpfcn) \ + HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, &((add)->fieldname), keylen_in, hashval, add, cmpfcn) + +#define HASH_ADD_INORDER(hh,head,fieldname,keylen_in,add,cmpfcn) \ + HASH_ADD_KEYPTR_INORDER(hh, head, &((add)->fieldname), keylen_in, add, cmpfcn) + +#define HASH_ADD_KEYPTR_BYHASHVALUE(hh,head,keyptr,keylen_in,hashval,add) \ +do { \ + unsigned _ha_bkt; \ + (add)->hh.hashv = (hashval); \ + (add)->hh.key = (char*) (keyptr); \ + (add)->hh.keylen = (unsigned) (keylen_in); \ + if (!(head)) { \ + (add)->hh.next = NULL; \ + (add)->hh.prev = NULL; \ + (head) = (add); \ + HASH_MAKE_TABLE(hh, head); \ + } else { \ + (add)->hh.tbl = (head)->hh.tbl; \ + HASH_APPEND_LIST(hh, head, add); \ + } \ + (head)->hh.tbl->num_items++; \ + HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _ha_bkt); \ + HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt], &(add)->hh); \ + HASH_BLOOM_ADD((head)->hh.tbl, hashval); \ + HASH_EMIT_KEY(hh, head, keyptr, keylen_in); \ + HASH_FSCK(hh, head); \ +} while (0) + +#define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add) \ +do { \ + unsigned _ha_hashv; \ + HASH_VALUE(keyptr, keylen_in, _ha_hashv); \ + HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, keyptr, keylen_in, _ha_hashv, add); \ +} while (0) + +#define HASH_ADD_BYHASHVALUE(hh,head,fieldname,keylen_in,hashval,add) \ + HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, add) + +#define HASH_ADD(hh,head,fieldname,keylen_in,add) \ + HASH_ADD_KEYPTR(hh, head, &((add)->fieldname), keylen_in, add) + +#define HASH_TO_BKT(hashv,num_bkts,bkt) \ +do { \ + bkt = ((hashv) & ((num_bkts) - 1U)); \ +} while (0) + +/* delete "delptr" from the hash table. + * "the usual" patch-up process for the app-order doubly-linked-list. + * The use of _hd_hh_del below deserves special explanation. + * These used to be expressed using (delptr) but that led to a bug + * if someone used the same symbol for the head and deletee, like + * HASH_DELETE(hh,users,users); + * We want that to work, but by changing the head (users) below + * we were forfeiting our ability to further refer to the deletee (users) + * in the patch-up process. Solution: use scratch space to + * copy the deletee pointer, then the latter references are via that + * scratch pointer rather than through the repointed (users) symbol. + */ +#define HASH_DELETE(hh,head,delptr) \ +do { \ + struct UT_hash_handle *_hd_hh_del; \ + if ( ((delptr)->hh.prev == NULL) && ((delptr)->hh.next == NULL) ) { \ + uthash_free((head)->hh.tbl->buckets, \ + (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket) ); \ + HASH_BLOOM_FREE((head)->hh.tbl); \ + uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ + head = NULL; \ + } else { \ + unsigned _hd_bkt; \ + _hd_hh_del = &((delptr)->hh); \ + if ((delptr) == ELMT_FROM_HH((head)->hh.tbl,(head)->hh.tbl->tail)) { \ + (head)->hh.tbl->tail = \ + (UT_hash_handle*)((ptrdiff_t)((delptr)->hh.prev) + \ + (head)->hh.tbl->hho); \ + } \ + if ((delptr)->hh.prev != NULL) { \ + ((UT_hash_handle*)((ptrdiff_t)((delptr)->hh.prev) + \ + (head)->hh.tbl->hho))->next = (delptr)->hh.next; \ + } else { \ + DECLTYPE_ASSIGN(head,(delptr)->hh.next); \ + } \ + if (_hd_hh_del->next != NULL) { \ + ((UT_hash_handle*)((ptrdiff_t)_hd_hh_del->next + \ + (head)->hh.tbl->hho))->prev = \ + _hd_hh_del->prev; \ + } \ + HASH_TO_BKT( _hd_hh_del->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \ + HASH_DEL_IN_BKT(hh,(head)->hh.tbl->buckets[_hd_bkt], _hd_hh_del); \ + (head)->hh.tbl->num_items--; \ + } \ + HASH_FSCK(hh,head); \ +} while (0) + + +/* convenience forms of HASH_FIND/HASH_ADD/HASH_DEL */ +#define HASH_FIND_STR(head,findstr,out) \ + HASH_FIND(hh,head,findstr,(unsigned)uthash_strlen(findstr),out) +#define HASH_ADD_STR(head,strfield,add) \ + HASH_ADD(hh,head,strfield[0],(unsigned)uthash_strlen(add->strfield),add) +#define HASH_REPLACE_STR(head,strfield,add,replaced) \ + HASH_REPLACE(hh,head,strfield[0],(unsigned)uthash_strlen(add->strfield),add,replaced) +#define HASH_FIND_INT(head,findint,out) \ + HASH_FIND(hh,head,findint,sizeof(int),out) +#define HASH_ADD_INT(head,intfield,add) \ + HASH_ADD(hh,head,intfield,sizeof(int),add) +#define HASH_REPLACE_INT(head,intfield,add,replaced) \ + HASH_REPLACE(hh,head,intfield,sizeof(int),add,replaced) +#define HASH_FIND_PTR(head,findptr,out) \ + HASH_FIND(hh,head,findptr,sizeof(void *),out) +#define HASH_ADD_PTR(head,ptrfield,add) \ + HASH_ADD(hh,head,ptrfield,sizeof(void *),add) +#define HASH_REPLACE_PTR(head,ptrfield,add,replaced) \ + HASH_REPLACE(hh,head,ptrfield,sizeof(void *),add,replaced) +#define HASH_DEL(head,delptr) \ + HASH_DELETE(hh,head,delptr) + +/* HASH_FSCK checks hash integrity on every add/delete when HASH_DEBUG is defined. + * This is for uthash developer only; it compiles away if HASH_DEBUG isn't defined. + */ +#ifdef HASH_DEBUG +#define HASH_OOPS(...) do { fprintf(stderr,__VA_ARGS__); exit(-1); } while (0) +#define HASH_FSCK(hh,head) \ +do { \ + struct UT_hash_handle *_thh; \ + if (head) { \ + unsigned _bkt_i; \ + unsigned _count; \ + char *_prev; \ + _count = 0; \ + for( _bkt_i = 0; _bkt_i < (head)->hh.tbl->num_buckets; _bkt_i++) { \ + unsigned _bkt_count = 0; \ + _thh = (head)->hh.tbl->buckets[_bkt_i].hh_head; \ + _prev = NULL; \ + while (_thh) { \ + if (_prev != (char*)(_thh->hh_prev)) { \ + HASH_OOPS("invalid hh_prev %p, actual %p\n", \ + _thh->hh_prev, _prev ); \ + } \ + _bkt_count++; \ + _prev = (char*)(_thh); \ + _thh = _thh->hh_next; \ + } \ + _count += _bkt_count; \ + if ((head)->hh.tbl->buckets[_bkt_i].count != _bkt_count) { \ + HASH_OOPS("invalid bucket count %u, actual %u\n", \ + (head)->hh.tbl->buckets[_bkt_i].count, _bkt_count); \ + } \ + } \ + if (_count != (head)->hh.tbl->num_items) { \ + HASH_OOPS("invalid hh item count %u, actual %u\n", \ + (head)->hh.tbl->num_items, _count ); \ + } \ + /* traverse hh in app order; check next/prev integrity, count */ \ + _count = 0; \ + _prev = NULL; \ + _thh = &(head)->hh; \ + while (_thh) { \ + _count++; \ + if (_prev !=(char*)(_thh->prev)) { \ + HASH_OOPS("invalid prev %p, actual %p\n", \ + _thh->prev, _prev ); \ + } \ + _prev = (char*)ELMT_FROM_HH((head)->hh.tbl, _thh); \ + _thh = ( _thh->next ? (UT_hash_handle*)((char*)(_thh->next) + \ + (head)->hh.tbl->hho) : NULL ); \ + } \ + if (_count != (head)->hh.tbl->num_items) { \ + HASH_OOPS("invalid app item count %u, actual %u\n", \ + (head)->hh.tbl->num_items, _count ); \ + } \ + } \ +} while (0) +#else +#define HASH_FSCK(hh,head) +#endif + +/* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to + * the descriptor to which this macro is defined for tuning the hash function. + * The app can #include to get the prototype for write(2). */ +#ifdef HASH_EMIT_KEYS +#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) \ +do { \ + unsigned _klen = fieldlen; \ + write(HASH_EMIT_KEYS, &_klen, sizeof(_klen)); \ + write(HASH_EMIT_KEYS, keyptr, (unsigned long)fieldlen); \ +} while (0) +#else +#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) +#endif + +/* default to Jenkin's hash unless overridden e.g. DHASH_FUNCTION=HASH_SAX */ +#ifdef HASH_FUNCTION +#define HASH_FCN HASH_FUNCTION +#else +#define HASH_FCN HASH_JEN +#endif + +/* The Bernstein hash function, used in Perl prior to v5.6. Note (x<<5+x)=x*33. */ +#define HASH_BER(key,keylen,hashv) \ +do { \ + unsigned _hb_keylen=(unsigned)keylen; \ + const unsigned char *_hb_key=(const unsigned char*)(key); \ + (hashv) = 0; \ + while (_hb_keylen-- != 0U) { \ + (hashv) = (((hashv) << 5) + (hashv)) + *_hb_key++; \ + } \ +} while (0) + + +/* SAX/FNV/OAT/JEN hash functions are macro variants of those listed at + * http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx */ +#define HASH_SAX(key,keylen,hashv) \ +do { \ + unsigned _sx_i; \ + const unsigned char *_hs_key=(const unsigned char*)(key); \ + hashv = 0; \ + for(_sx_i=0; _sx_i < keylen; _sx_i++) { \ + hashv ^= (hashv << 5) + (hashv >> 2) + _hs_key[_sx_i]; \ + } \ +} while (0) +/* FNV-1a variation */ +#define HASH_FNV(key,keylen,hashv) \ +do { \ + unsigned _fn_i; \ + const unsigned char *_hf_key=(const unsigned char*)(key); \ + hashv = 2166136261U; \ + for(_fn_i=0; _fn_i < keylen; _fn_i++) { \ + hashv = hashv ^ _hf_key[_fn_i]; \ + hashv = hashv * 16777619U; \ + } \ +} while (0) + +#define HASH_OAT(key,keylen,hashv) \ +do { \ + unsigned _ho_i; \ + const unsigned char *_ho_key=(const unsigned char*)(key); \ + hashv = 0; \ + for(_ho_i=0; _ho_i < keylen; _ho_i++) { \ + hashv += _ho_key[_ho_i]; \ + hashv += (hashv << 10); \ + hashv ^= (hashv >> 6); \ + } \ + hashv += (hashv << 3); \ + hashv ^= (hashv >> 11); \ + hashv += (hashv << 15); \ +} while (0) + +#define HASH_JEN_MIX(a,b,c) \ +do { \ + a -= b; a -= c; a ^= ( c >> 13 ); \ + b -= c; b -= a; b ^= ( a << 8 ); \ + c -= a; c -= b; c ^= ( b >> 13 ); \ + a -= b; a -= c; a ^= ( c >> 12 ); \ + b -= c; b -= a; b ^= ( a << 16 ); \ + c -= a; c -= b; c ^= ( b >> 5 ); \ + a -= b; a -= c; a ^= ( c >> 3 ); \ + b -= c; b -= a; b ^= ( a << 10 ); \ + c -= a; c -= b; c ^= ( b >> 15 ); \ +} while (0) + +#define HASH_JEN(key,keylen,hashv) \ +do { \ + unsigned _hj_i,_hj_j,_hj_k; \ + unsigned const char *_hj_key=(unsigned const char*)(key); \ + hashv = 0xfeedbeefu; \ + _hj_i = _hj_j = 0x9e3779b9u; \ + _hj_k = (unsigned)(keylen); \ + while (_hj_k >= 12U) { \ + _hj_i += (_hj_key[0] + ( (unsigned)_hj_key[1] << 8 ) \ + + ( (unsigned)_hj_key[2] << 16 ) \ + + ( (unsigned)_hj_key[3] << 24 ) ); \ + _hj_j += (_hj_key[4] + ( (unsigned)_hj_key[5] << 8 ) \ + + ( (unsigned)_hj_key[6] << 16 ) \ + + ( (unsigned)_hj_key[7] << 24 ) ); \ + hashv += (_hj_key[8] + ( (unsigned)_hj_key[9] << 8 ) \ + + ( (unsigned)_hj_key[10] << 16 ) \ + + ( (unsigned)_hj_key[11] << 24 ) ); \ + \ + HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ + \ + _hj_key += 12; \ + _hj_k -= 12U; \ + } \ + hashv += (unsigned)(keylen); \ + switch ( _hj_k ) { \ + case 11: hashv += ( (unsigned)_hj_key[10] << 24 ); /* FALLTHROUGH */ \ + case 10: hashv += ( (unsigned)_hj_key[9] << 16 ); /* FALLTHROUGH */ \ + case 9: hashv += ( (unsigned)_hj_key[8] << 8 ); /* FALLTHROUGH */ \ + case 8: _hj_j += ( (unsigned)_hj_key[7] << 24 ); /* FALLTHROUGH */ \ + case 7: _hj_j += ( (unsigned)_hj_key[6] << 16 ); /* FALLTHROUGH */ \ + case 6: _hj_j += ( (unsigned)_hj_key[5] << 8 ); /* FALLTHROUGH */ \ + case 5: _hj_j += _hj_key[4]; /* FALLTHROUGH */ \ + case 4: _hj_i += ( (unsigned)_hj_key[3] << 24 ); /* FALLTHROUGH */ \ + case 3: _hj_i += ( (unsigned)_hj_key[2] << 16 ); /* FALLTHROUGH */ \ + case 2: _hj_i += ( (unsigned)_hj_key[1] << 8 ); /* FALLTHROUGH */ \ + case 1: _hj_i += _hj_key[0]; \ + } \ + HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ +} while (0) + +/* The Paul Hsieh hash function */ +#undef get16bits +#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \ + || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__) +#define get16bits(d) (*((const uint16_t *) (d))) +#endif + +#if !defined (get16bits) +#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8) \ + +(uint32_t)(((const uint8_t *)(d))[0]) ) +#endif +#define HASH_SFH(key,keylen,hashv) \ +do { \ + unsigned const char *_sfh_key=(unsigned const char*)(key); \ + uint32_t _sfh_tmp, _sfh_len = (uint32_t)keylen; \ + \ + unsigned _sfh_rem = _sfh_len & 3U; \ + _sfh_len >>= 2; \ + hashv = 0xcafebabeu; \ + \ + /* Main loop */ \ + for (;_sfh_len > 0U; _sfh_len--) { \ + hashv += get16bits (_sfh_key); \ + _sfh_tmp = ((uint32_t)(get16bits (_sfh_key+2)) << 11) ^ hashv; \ + hashv = (hashv << 16) ^ _sfh_tmp; \ + _sfh_key += 2U*sizeof (uint16_t); \ + hashv += hashv >> 11; \ + } \ + \ + /* Handle end cases */ \ + switch (_sfh_rem) { \ + case 3: hashv += get16bits (_sfh_key); \ + hashv ^= hashv << 16; \ + hashv ^= (uint32_t)(_sfh_key[sizeof (uint16_t)]) << 18; \ + hashv += hashv >> 11; \ + break; \ + case 2: hashv += get16bits (_sfh_key); \ + hashv ^= hashv << 11; \ + hashv += hashv >> 17; \ + break; \ + case 1: hashv += *_sfh_key; \ + hashv ^= hashv << 10; \ + hashv += hashv >> 1; \ + } \ + \ + /* Force "avalanching" of final 127 bits */ \ + hashv ^= hashv << 3; \ + hashv += hashv >> 5; \ + hashv ^= hashv << 4; \ + hashv += hashv >> 17; \ + hashv ^= hashv << 25; \ + hashv += hashv >> 6; \ +} while (0) + +#ifdef HASH_USING_NO_STRICT_ALIASING +/* The MurmurHash exploits some CPU's (x86,x86_64) tolerance for unaligned reads. + * For other types of CPU's (e.g. Sparc) an unaligned read causes a bus error. + * MurmurHash uses the faster approach only on CPU's where we know it's safe. + * + * Note the preprocessor built-in defines can be emitted using: + * + * gcc -m64 -dM -E - < /dev/null (on gcc) + * cc -## a.c (where a.c is a simple test file) (Sun Studio) + */ +#if (defined(__i386__) || defined(__x86_64__) || defined(_M_IX86)) +#define MUR_GETBLOCK(p,i) p[i] +#else /* non intel */ +#define MUR_PLUS0_ALIGNED(p) (((unsigned long)p & 3UL) == 0UL) +#define MUR_PLUS1_ALIGNED(p) (((unsigned long)p & 3UL) == 1UL) +#define MUR_PLUS2_ALIGNED(p) (((unsigned long)p & 3UL) == 2UL) +#define MUR_PLUS3_ALIGNED(p) (((unsigned long)p & 3UL) == 3UL) +#define WP(p) ((uint32_t*)((unsigned long)(p) & ~3UL)) +#if (defined(__BIG_ENDIAN__) || defined(SPARC) || defined(__ppc__) || defined(__ppc64__)) +#define MUR_THREE_ONE(p) ((((*WP(p))&0x00ffffff) << 8) | (((*(WP(p)+1))&0xff000000) >> 24)) +#define MUR_TWO_TWO(p) ((((*WP(p))&0x0000ffff) <<16) | (((*(WP(p)+1))&0xffff0000) >> 16)) +#define MUR_ONE_THREE(p) ((((*WP(p))&0x000000ff) <<24) | (((*(WP(p)+1))&0xffffff00) >> 8)) +#else /* assume little endian non-intel */ +#define MUR_THREE_ONE(p) ((((*WP(p))&0xffffff00) >> 8) | (((*(WP(p)+1))&0x000000ff) << 24)) +#define MUR_TWO_TWO(p) ((((*WP(p))&0xffff0000) >>16) | (((*(WP(p)+1))&0x0000ffff) << 16)) +#define MUR_ONE_THREE(p) ((((*WP(p))&0xff000000) >>24) | (((*(WP(p)+1))&0x00ffffff) << 8)) +#endif +#define MUR_GETBLOCK(p,i) (MUR_PLUS0_ALIGNED(p) ? ((p)[i]) : \ + (MUR_PLUS1_ALIGNED(p) ? MUR_THREE_ONE(p) : \ + (MUR_PLUS2_ALIGNED(p) ? MUR_TWO_TWO(p) : \ + MUR_ONE_THREE(p)))) +#endif +#define MUR_ROTL32(x,r) (((x) << (r)) | ((x) >> (32 - (r)))) +#define MUR_FMIX(_h) \ +do { \ + _h ^= _h >> 16; \ + _h *= 0x85ebca6bu; \ + _h ^= _h >> 13; \ + _h *= 0xc2b2ae35u; \ + _h ^= _h >> 16; \ +} while (0) + +#define HASH_MUR(key,keylen,hashv) \ +do { \ + const uint8_t *_mur_data = (const uint8_t*)(key); \ + const int _mur_nblocks = (int)(keylen) / 4; \ + uint32_t _mur_h1 = 0xf88D5353u; \ + uint32_t _mur_c1 = 0xcc9e2d51u; \ + uint32_t _mur_c2 = 0x1b873593u; \ + uint32_t _mur_k1 = 0; \ + const uint8_t *_mur_tail; \ + const uint32_t *_mur_blocks = (const uint32_t*)(_mur_data+(_mur_nblocks*4)); \ + int _mur_i; \ + for(_mur_i = -_mur_nblocks; _mur_i!=0; _mur_i++) { \ + _mur_k1 = MUR_GETBLOCK(_mur_blocks,_mur_i); \ + _mur_k1 *= _mur_c1; \ + _mur_k1 = MUR_ROTL32(_mur_k1,15); \ + _mur_k1 *= _mur_c2; \ + \ + _mur_h1 ^= _mur_k1; \ + _mur_h1 = MUR_ROTL32(_mur_h1,13); \ + _mur_h1 = (_mur_h1*5U) + 0xe6546b64u; \ + } \ + _mur_tail = (const uint8_t*)(_mur_data + (_mur_nblocks*4)); \ + _mur_k1=0; \ + switch((keylen) & 3U) { \ + case 3: _mur_k1 ^= (uint32_t)_mur_tail[2] << 16; /* FALLTHROUGH */ \ + case 2: _mur_k1 ^= (uint32_t)_mur_tail[1] << 8; /* FALLTHROUGH */ \ + case 1: _mur_k1 ^= (uint32_t)_mur_tail[0]; \ + _mur_k1 *= _mur_c1; \ + _mur_k1 = MUR_ROTL32(_mur_k1,15); \ + _mur_k1 *= _mur_c2; \ + _mur_h1 ^= _mur_k1; \ + } \ + _mur_h1 ^= (uint32_t)(keylen); \ + MUR_FMIX(_mur_h1); \ + hashv = _mur_h1; \ +} while (0) +#endif /* HASH_USING_NO_STRICT_ALIASING */ + +/* iterate over items in a known bucket to find desired item */ +#define HASH_FIND_IN_BKT(tbl,hh,head,keyptr,keylen_in,hashval,out) \ +do { \ + if ((head).hh_head != NULL) { \ + DECLTYPE_ASSIGN(out, ELMT_FROM_HH(tbl, (head).hh_head)); \ + } else { \ + (out) = NULL; \ + } \ + while ((out) != NULL) { \ + if ((out)->hh.hashv == (hashval) && (out)->hh.keylen == (keylen_in)) { \ + if (uthash_memcmp((out)->hh.key, keyptr, keylen_in) == 0) { \ + break; \ + } \ + } \ + if ((out)->hh.hh_next != NULL) { \ + DECLTYPE_ASSIGN(out, ELMT_FROM_HH(tbl, (out)->hh.hh_next)); \ + } else { \ + (out) = NULL; \ + } \ + } \ +} while (0) + +/* add an item to a bucket */ +#define HASH_ADD_TO_BKT(head,addhh) \ +do { \ + head.count++; \ + (addhh)->hh_next = head.hh_head; \ + (addhh)->hh_prev = NULL; \ + if (head.hh_head != NULL) { (head).hh_head->hh_prev = (addhh); } \ + (head).hh_head=addhh; \ + if ((head.count >= ((head.expand_mult+1U) * HASH_BKT_CAPACITY_THRESH)) \ + && ((addhh)->tbl->noexpand != 1U)) { \ + HASH_EXPAND_BUCKETS((addhh)->tbl); \ + } \ +} while (0) + +/* remove an item from a given bucket */ +#define HASH_DEL_IN_BKT(hh,head,hh_del) \ + (head).count--; \ + if ((head).hh_head == hh_del) { \ + (head).hh_head = hh_del->hh_next; \ + } \ + if (hh_del->hh_prev) { \ + hh_del->hh_prev->hh_next = hh_del->hh_next; \ + } \ + if (hh_del->hh_next) { \ + hh_del->hh_next->hh_prev = hh_del->hh_prev; \ + } + +/* Bucket expansion has the effect of doubling the number of buckets + * and redistributing the items into the new buckets. Ideally the + * items will distribute more or less evenly into the new buckets + * (the extent to which this is true is a measure of the quality of + * the hash function as it applies to the key domain). + * + * With the items distributed into more buckets, the chain length + * (item count) in each bucket is reduced. Thus by expanding buckets + * the hash keeps a bound on the chain length. This bounded chain + * length is the essence of how a hash provides constant time lookup. + * + * The calculation of tbl->ideal_chain_maxlen below deserves some + * explanation. First, keep in mind that we're calculating the ideal + * maximum chain length based on the *new* (doubled) bucket count. + * In fractions this is just n/b (n=number of items,b=new num buckets). + * Since the ideal chain length is an integer, we want to calculate + * ceil(n/b). We don't depend on floating point arithmetic in this + * hash, so to calculate ceil(n/b) with integers we could write + * + * ceil(n/b) = (n/b) + ((n%b)?1:0) + * + * and in fact a previous version of this hash did just that. + * But now we have improved things a bit by recognizing that b is + * always a power of two. We keep its base 2 log handy (call it lb), + * so now we can write this with a bit shift and logical AND: + * + * ceil(n/b) = (n>>lb) + ( (n & (b-1)) ? 1:0) + * + */ +#define HASH_EXPAND_BUCKETS(tbl) \ +do { \ + unsigned _he_bkt; \ + unsigned _he_bkt_i; \ + struct UT_hash_handle *_he_thh, *_he_hh_nxt; \ + UT_hash_bucket *_he_new_buckets, *_he_newbkt; \ + _he_new_buckets = (UT_hash_bucket*)uthash_malloc( \ + 2UL * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \ + if (!_he_new_buckets) { uthash_fatal( "out of memory"); } \ + memset(_he_new_buckets, 0, \ + 2UL * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \ + tbl->ideal_chain_maxlen = \ + (tbl->num_items >> (tbl->log2_num_buckets+1U)) + \ + (((tbl->num_items & ((tbl->num_buckets*2U)-1U)) != 0U) ? 1U : 0U); \ + tbl->nonideal_items = 0; \ + for(_he_bkt_i = 0; _he_bkt_i < tbl->num_buckets; _he_bkt_i++) \ + { \ + _he_thh = tbl->buckets[ _he_bkt_i ].hh_head; \ + while (_he_thh != NULL) { \ + _he_hh_nxt = _he_thh->hh_next; \ + HASH_TO_BKT( _he_thh->hashv, tbl->num_buckets*2U, _he_bkt); \ + _he_newbkt = &(_he_new_buckets[ _he_bkt ]); \ + if (++(_he_newbkt->count) > tbl->ideal_chain_maxlen) { \ + tbl->nonideal_items++; \ + _he_newbkt->expand_mult = _he_newbkt->count / \ + tbl->ideal_chain_maxlen; \ + } \ + _he_thh->hh_prev = NULL; \ + _he_thh->hh_next = _he_newbkt->hh_head; \ + if (_he_newbkt->hh_head != NULL) { _he_newbkt->hh_head->hh_prev = \ + _he_thh; } \ + _he_newbkt->hh_head = _he_thh; \ + _he_thh = _he_hh_nxt; \ + } \ + } \ + uthash_free( tbl->buckets, tbl->num_buckets*sizeof(struct UT_hash_bucket) ); \ + tbl->num_buckets *= 2U; \ + tbl->log2_num_buckets++; \ + tbl->buckets = _he_new_buckets; \ + tbl->ineff_expands = (tbl->nonideal_items > (tbl->num_items >> 1)) ? \ + (tbl->ineff_expands+1U) : 0U; \ + if (tbl->ineff_expands > 1U) { \ + tbl->noexpand=1; \ + uthash_noexpand_fyi(tbl); \ + } \ + uthash_expand_fyi(tbl); \ +} while (0) + + +/* This is an adaptation of Simon Tatham's O(n log(n)) mergesort */ +/* Note that HASH_SORT assumes the hash handle name to be hh. + * HASH_SRT was added to allow the hash handle name to be passed in. */ +#define HASH_SORT(head,cmpfcn) HASH_SRT(hh,head,cmpfcn) +#define HASH_SRT(hh,head,cmpfcn) \ +do { \ + unsigned _hs_i; \ + unsigned _hs_looping,_hs_nmerges,_hs_insize,_hs_psize,_hs_qsize; \ + struct UT_hash_handle *_hs_p, *_hs_q, *_hs_e, *_hs_list, *_hs_tail; \ + if (head != NULL) { \ + _hs_insize = 1; \ + _hs_looping = 1; \ + _hs_list = &((head)->hh); \ + while (_hs_looping != 0U) { \ + _hs_p = _hs_list; \ + _hs_list = NULL; \ + _hs_tail = NULL; \ + _hs_nmerges = 0; \ + while (_hs_p != NULL) { \ + _hs_nmerges++; \ + _hs_q = _hs_p; \ + _hs_psize = 0; \ + for ( _hs_i = 0; _hs_i < _hs_insize; _hs_i++ ) { \ + _hs_psize++; \ + _hs_q = (UT_hash_handle*)((_hs_q->next != NULL) ? \ + ((void*)((char*)(_hs_q->next) + \ + (head)->hh.tbl->hho)) : NULL); \ + if (! (_hs_q) ) { break; } \ + } \ + _hs_qsize = _hs_insize; \ + while ((_hs_psize > 0U) || ((_hs_qsize > 0U) && (_hs_q != NULL))) {\ + if (_hs_psize == 0U) { \ + _hs_e = _hs_q; \ + _hs_q = (UT_hash_handle*)((_hs_q->next != NULL) ? \ + ((void*)((char*)(_hs_q->next) + \ + (head)->hh.tbl->hho)) : NULL); \ + _hs_qsize--; \ + } else if ( (_hs_qsize == 0U) || (_hs_q == NULL) ) { \ + _hs_e = _hs_p; \ + if (_hs_p != NULL){ \ + _hs_p = (UT_hash_handle*)((_hs_p->next != NULL) ? \ + ((void*)((char*)(_hs_p->next) + \ + (head)->hh.tbl->hho)) : NULL); \ + } \ + _hs_psize--; \ + } else if (( \ + cmpfcn(DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_p)), \ + DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_q))) \ + ) <= 0) { \ + _hs_e = _hs_p; \ + if (_hs_p != NULL){ \ + _hs_p = (UT_hash_handle*)((_hs_p->next != NULL) ? \ + ((void*)((char*)(_hs_p->next) + \ + (head)->hh.tbl->hho)) : NULL); \ + } \ + _hs_psize--; \ + } else { \ + _hs_e = _hs_q; \ + _hs_q = (UT_hash_handle*)((_hs_q->next != NULL) ? \ + ((void*)((char*)(_hs_q->next) + \ + (head)->hh.tbl->hho)) : NULL); \ + _hs_qsize--; \ + } \ + if ( _hs_tail != NULL ) { \ + _hs_tail->next = ((_hs_e != NULL) ? \ + ELMT_FROM_HH((head)->hh.tbl,_hs_e) : NULL); \ + } else { \ + _hs_list = _hs_e; \ + } \ + if (_hs_e != NULL) { \ + _hs_e->prev = ((_hs_tail != NULL) ? \ + ELMT_FROM_HH((head)->hh.tbl,_hs_tail) : NULL); \ + } \ + _hs_tail = _hs_e; \ + } \ + _hs_p = _hs_q; \ + } \ + if (_hs_tail != NULL){ \ + _hs_tail->next = NULL; \ + } \ + if ( _hs_nmerges <= 1U ) { \ + _hs_looping=0; \ + (head)->hh.tbl->tail = _hs_tail; \ + DECLTYPE_ASSIGN(head,ELMT_FROM_HH((head)->hh.tbl, _hs_list)); \ + } \ + _hs_insize *= 2U; \ + } \ + HASH_FSCK(hh,head); \ + } \ +} while (0) + +/* This function selects items from one hash into another hash. + * The end result is that the selected items have dual presence + * in both hashes. There is no copy of the items made; rather + * they are added into the new hash through a secondary hash + * hash handle that must be present in the structure. */ +#define HASH_SELECT(hh_dst, dst, hh_src, src, cond) \ +do { \ + unsigned _src_bkt, _dst_bkt; \ + void *_last_elt=NULL, *_elt; \ + UT_hash_handle *_src_hh, *_dst_hh, *_last_elt_hh=NULL; \ + ptrdiff_t _dst_hho = ((char*)(&(dst)->hh_dst) - (char*)(dst)); \ + if (src != NULL) { \ + for(_src_bkt=0; _src_bkt < (src)->hh_src.tbl->num_buckets; _src_bkt++) { \ + for(_src_hh = (src)->hh_src.tbl->buckets[_src_bkt].hh_head; \ + _src_hh != NULL; \ + _src_hh = _src_hh->hh_next) { \ + _elt = ELMT_FROM_HH((src)->hh_src.tbl, _src_hh); \ + if (cond(_elt)) { \ + _dst_hh = (UT_hash_handle*)(((char*)_elt) + _dst_hho); \ + _dst_hh->key = _src_hh->key; \ + _dst_hh->keylen = _src_hh->keylen; \ + _dst_hh->hashv = _src_hh->hashv; \ + _dst_hh->prev = _last_elt; \ + _dst_hh->next = NULL; \ + if (_last_elt_hh != NULL) { _last_elt_hh->next = _elt; } \ + if (dst == NULL) { \ + DECLTYPE_ASSIGN(dst,_elt); \ + HASH_MAKE_TABLE(hh_dst,dst); \ + } else { \ + _dst_hh->tbl = (dst)->hh_dst.tbl; \ + } \ + HASH_TO_BKT(_dst_hh->hashv, _dst_hh->tbl->num_buckets, _dst_bkt); \ + HASH_ADD_TO_BKT(_dst_hh->tbl->buckets[_dst_bkt],_dst_hh); \ + (dst)->hh_dst.tbl->num_items++; \ + _last_elt = _elt; \ + _last_elt_hh = _dst_hh; \ + } \ + } \ + } \ + } \ + HASH_FSCK(hh_dst,dst); \ +} while (0) + +#define HASH_CLEAR(hh,head) \ +do { \ + if (head != NULL) { \ + uthash_free((head)->hh.tbl->buckets, \ + (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket)); \ + HASH_BLOOM_FREE((head)->hh.tbl); \ + uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ + (head)=NULL; \ + } \ +} while (0) + +#define HASH_OVERHEAD(hh,head) \ + ((head != NULL) ? ( \ + (size_t)(((head)->hh.tbl->num_items * sizeof(UT_hash_handle)) + \ + ((head)->hh.tbl->num_buckets * sizeof(UT_hash_bucket)) + \ + sizeof(UT_hash_table) + \ + (HASH_BLOOM_BYTELEN))) : 0U) + +#ifdef NO_DECLTYPE +#define HASH_ITER(hh,head,el,tmp) \ +for(((el)=(head)), ((*(char**)(&(tmp)))=(char*)((head!=NULL)?(head)->hh.next:NULL)); \ + (el) != NULL; ((el)=(tmp)), ((*(char**)(&(tmp)))=(char*)((tmp!=NULL)?(tmp)->hh.next:NULL))) +#else +#define HASH_ITER(hh,head,el,tmp) \ +for(((el)=(head)), ((tmp)=DECLTYPE(el)((head!=NULL)?(head)->hh.next:NULL)); \ + (el) != NULL; ((el)=(tmp)), ((tmp)=DECLTYPE(el)((tmp!=NULL)?(tmp)->hh.next:NULL))) +#endif + +/* obtain a count of items in the hash */ +#define HASH_COUNT(head) HASH_CNT(hh,head) +#define HASH_CNT(hh,head) ((head != NULL)?((head)->hh.tbl->num_items):0U) + +typedef struct UT_hash_bucket { + struct UT_hash_handle *hh_head; + unsigned count; + + /* expand_mult is normally set to 0. In this situation, the max chain length + * threshold is enforced at its default value, HASH_BKT_CAPACITY_THRESH. (If + * the bucket's chain exceeds this length, bucket expansion is triggered). + * However, setting expand_mult to a non-zero value delays bucket expansion + * (that would be triggered by additions to this particular bucket) + * until its chain length reaches a *multiple* of HASH_BKT_CAPACITY_THRESH. + * (The multiplier is simply expand_mult+1). The whole idea of this + * multiplier is to reduce bucket expansions, since they are expensive, in + * situations where we know that a particular bucket tends to be overused. + * It is better to let its chain length grow to a longer yet-still-bounded + * value, than to do an O(n) bucket expansion too often. + */ + unsigned expand_mult; + +} UT_hash_bucket; + +/* random signature used only to find hash tables in external analysis */ +#define HASH_SIGNATURE 0xa0111fe1u +#define HASH_BLOOM_SIGNATURE 0xb12220f2u + +typedef struct UT_hash_table { + UT_hash_bucket *buckets; + unsigned num_buckets, log2_num_buckets; + unsigned num_items; + struct UT_hash_handle *tail; /* tail hh in app order, for fast append */ + ptrdiff_t hho; /* hash handle offset (byte pos of hash handle in element */ + + /* in an ideal situation (all buckets used equally), no bucket would have + * more than ceil(#items/#buckets) items. that's the ideal chain length. */ + unsigned ideal_chain_maxlen; + + /* nonideal_items is the number of items in the hash whose chain position + * exceeds the ideal chain maxlen. these items pay the penalty for an uneven + * hash distribution; reaching them in a chain traversal takes >ideal steps */ + unsigned nonideal_items; + + /* ineffective expands occur when a bucket doubling was performed, but + * afterward, more than half the items in the hash had nonideal chain + * positions. If this happens on two consecutive expansions we inhibit any + * further expansion, as it's not helping; this happens when the hash + * function isn't a good fit for the key domain. When expansion is inhibited + * the hash will still work, albeit no longer in constant time. */ + unsigned ineff_expands, noexpand; + + uint32_t signature; /* used only to find hash tables in external analysis */ +#ifdef HASH_BLOOM + uint32_t bloom_sig; /* used only to test bloom exists in external analysis */ + uint8_t *bloom_bv; + uint8_t bloom_nbits; +#endif + +} UT_hash_table; + +typedef struct UT_hash_handle { + struct UT_hash_table *tbl; + void *prev; /* prev element in app order */ + void *next; /* next element in app order */ + struct UT_hash_handle *hh_prev; /* previous hh in bucket order */ + struct UT_hash_handle *hh_next; /* next hh in bucket order */ + void *key; /* ptr to enclosing struct's key */ + unsigned keylen; /* enclosing struct's key len */ + unsigned hashv; /* result of hash-fcn(key) */ +} UT_hash_handle; + +#endif /* UTHASH_H */ diff --git a/shadowsocksr-libev/src/server/utils.c b/shadowsocksr-libev/src/server/utils.c new file mode 100644 index 000000000..14a60c7f3 --- /dev/null +++ b/shadowsocksr-libev/src/server/utils.c @@ -0,0 +1,448 @@ +/* + * utils.c - Misc utilities + * + * Copyright (C) 2013 - 2016, Max Lv + * + * This file is part of the shadowsocks-libev. + * + * shadowsocks-libev is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * shadowsocks-libev 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with shadowsocks-libev; see the file COPYING. If not, see + * . + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#ifndef __MINGW32__ +#include +#include +#endif + +#include +#include + +#include "utils.h" + +#ifdef HAVE_SETRLIMIT +#include +#include +#endif + +#define INT_DIGITS 19 /* enough for 64 bit integer */ + +#ifdef LIB_ONLY +FILE *logfile; +#endif + +#ifdef HAS_SYSLOG +int use_syslog = 0; +#endif + +#ifndef __MINGW32__ +void +ERROR(const char *s) +{ + char *msg = strerror(errno); + LOGE("%s: %s", s, msg); +} + +#endif + +int use_tty = 1; + +char * +ss_itoa(int i) +{ + /* Room for INT_DIGITS digits, - and '\0' */ + static char buf[INT_DIGITS + 2]; + char *p = buf + INT_DIGITS + 1; /* points to terminating '\0' */ + if (i >= 0) { + do { + *--p = '0' + (i % 10); + i /= 10; + } while (i != 0); + return p; + } else { /* i < 0 */ + do { + *--p = '0' - (i % 10); + i /= 10; + } while (i != 0); + *--p = '-'; + } + return p; +} + +int +ss_isnumeric(const char *s) { + if (!s || !*s) + return 0; + while (isdigit(*s)) + ++s; + return *s == '\0'; +} + +/* + * setuid() and setgid() for a specified user. + */ +int +run_as(const char *user) +{ +#ifndef __MINGW32__ + if (user[0]) { + /* Convert user to a long integer if it is a non-negative number. + * -1 means it is a user name. */ + long uid = -1; + if (ss_isnumeric(user)) { + errno = 0; + char *endptr; + uid = strtol(user, &endptr, 10); + if (errno || endptr == user) + uid = -1; + } + +#ifdef HAVE_GETPWNAM_R + struct passwd pwdbuf, *pwd; + memset(&pwdbuf, 0, sizeof(struct passwd)); + size_t buflen; + int err; + + for (buflen = 128;; buflen *= 2) { + char buf[buflen]; /* variable length array */ + + /* Note that we use getpwnam_r() instead of getpwnam(), + * which returns its result in a statically allocated buffer and + * cannot be considered thread safe. */ + err = uid >= 0 ? getpwuid_r((uid_t)uid, &pwdbuf, buf, buflen, &pwd) + : getpwnam_r(user, &pwdbuf, buf, buflen, &pwd); + + if (err == 0 && pwd) { + /* setgid first, because we may not be allowed to do it anymore after setuid */ + if (setgid(pwd->pw_gid) != 0) { + LOGE( + "Could not change group id to that of run_as user '%s': %s", + pwd->pw_name, strerror(errno)); + return 0; + } + + if (initgroups(pwd->pw_name, pwd->pw_gid) == -1) { + LOGE("Could not change supplementary groups for user '%s'.", pwd->pw_name); + return 0; + } + + if (setuid(pwd->pw_uid) != 0) { + LOGE( + "Could not change user id to that of run_as user '%s': %s", + pwd->pw_name, strerror(errno)); + return 0; + } + break; + } else if (err != ERANGE) { + if (err) { + LOGE("run_as user '%s' could not be found: %s", user, + strerror(err)); + } else { + LOGE("run_as user '%s' could not be found.", user); + } + return 0; + } else if (buflen >= 16 * 1024) { + /* If getpwnam_r() seems defective, call it quits rather than + * keep on allocating ever larger buffers until we crash. */ + LOGE( + "getpwnam_r() requires more than %u bytes of buffer space.", + (unsigned)buflen); + return 0; + } + /* Else try again with larger buffer. */ + } +#else + /* No getpwnam_r() :-( We'll use getpwnam() and hope for the best. */ + struct passwd *pwd; + + if (!(pwd = uid >=0 ? getpwuid((uid_t)uid) : getpwnam(user))) { + LOGE("run_as user %s could not be found.", user); + return 0; + } + /* setgid first, because we may not allowed to do it anymore after setuid */ + if (setgid(pwd->pw_gid) != 0) { + LOGE("Could not change group id to that of run_as user '%s': %s", + pwd->pw_name, strerror(errno)); + return 0; + } + if (initgroups(pwd->pw_name, pwd->pw_gid) == -1) { + LOGE("Could not change supplementary groups for user '%s'.", pwd->pw_name); + return 0; + } + if (setuid(pwd->pw_uid) != 0) { + LOGE("Could not change user id to that of run_as user '%s': %s", + pwd->pw_name, strerror(errno)); + return 0; + } +#endif + } + +#endif // __MINGW32__ + return 1; +} + +char * +ss_strndup(const char *s, size_t n) +{ + size_t len = strlen(s); + char *ret; + + if (len <= n) { + return strdup(s); + } + + ret = ss_malloc(n + 1); + strncpy(ret, s, n); + ret[n] = '\0'; + return ret; +} + +void +FATAL(const char *msg) +{ + LOGE("%s", msg); + exit(-1); +} + +void * +ss_malloc(size_t size) +{ + void *tmp = malloc(size); + if (tmp == NULL) + exit(EXIT_FAILURE); + return tmp; +} + +void * +ss_realloc(void *ptr, size_t new_size) +{ + void *new = realloc(ptr, new_size); + if (new == NULL) { + free(ptr); + ptr = NULL; + exit(EXIT_FAILURE); + } + return new; +} + +void +usage() +{ + printf("\n"); + printf("shadowsocks-libev %s with %s\n\n", VERSION, USING_CRYPTO); + printf( + " maintained by Max Lv and Linus Yang \n\n"); + printf(" usage:\n\n"); +#ifdef MODULE_LOCAL + printf(" ss-local\n"); +#elif MODULE_REMOTE + printf(" ss-server\n"); +#elif MODULE_TUNNEL + printf(" ss-tunnel\n"); +#elif MODULE_REDIR + printf(" ss-redir\n"); +#elif MODULE_MANAGER + printf(" ss-manager\n"); +#endif + printf("\n"); + printf( + " -s Host name or IP address of your remote server.\n"); + printf( + " -p Port number of your remote server.\n"); + printf( + " -l Port number of your local server.\n"); + printf( + " -k Password of your remote server.\n"); + printf( + " -m Encrypt method: table, rc4, rc4-md5,\n"); + printf( + " aes-128-cfb, aes-192-cfb, aes-256-cfb,\n"); + printf( + " aes-128-ctr, aes-192-ctr, aes-256-ctr,\n"); + printf( + " bf-cfb, camellia-128-cfb, camellia-192-cfb,\n"); + printf( + " camellia-256-cfb, cast5-cfb, des-cfb,\n"); + printf( + " idea-cfb, rc2-cfb, seed-cfb, salsa20,\n"); + printf( + " chacha20 and chacha20-ietf.\n"); + printf( + " The default cipher is rc4-md5.\n"); + printf("\n"); + printf( + " [-a ] Run as another user.\n"); + printf( + " [-f ] The file path to store pid.\n"); + printf( + " [-t ] Socket timeout in seconds.\n"); + printf( + " [-c ] The path to config file.\n"); +#ifdef HAVE_SETRLIMIT + printf( + " [-n ] Max number of open files.\n"); +#endif +#ifndef MODULE_REDIR + printf( + " [-i ] Network interface to bind.\n"); +#endif + printf( + " [-b ] Local address to bind.\n"); + printf("\n"); + printf( + " [-u] Enable UDP relay.\n"); +#ifdef MODULE_REDIR + printf( + " TPROXY is required in redir mode.\n"); +#endif + printf( + " [-U] Enable UDP relay and disable TCP relay.\n"); + printf( + " [-A] Enable onetime authentication.\n"); +#ifdef MODULE_REMOTE + printf( + " [-6] Resovle hostname to IPv6 address first.\n"); +#endif + printf("\n"); +#ifdef MODULE_TUNNEL + printf( + " [-L :] Destination server address and port\n"); + printf( + " for local port forwarding.\n"); +#endif +#ifdef MODULE_REMOTE + printf( + " [-d ] Name servers for internal DNS resolver.\n"); +#endif +#if defined(MODULE_REMOTE) || defined(MODULE_LOCAL) + printf( + " [--fast-open] Enable TCP fast open.\n"); + printf( + " with Linux kernel > 3.7.0.\n"); + printf( + " [--acl ] Path to ACL (Access Control List).\n"); +#endif +#if defined(MODULE_REMOTE) || defined(MODULE_MANAGER) + printf( + " [--manager-address ] UNIX domain socket address.\n"); +#endif +#ifdef MODULE_MANAGER + printf( + " [--executable ] Path to the executable of ss-server.\n"); +#endif + printf( + " [--mtu ] MTU of your network interface.\n"); +#ifdef __linux__ + printf( + " [--mptcp] Enable Multipath TCP on MPTCP Kernel.\n"); +#ifdef MODULE_REMOTE + printf( + " [--firewall] Setup firewall rules for auto blocking.\n"); +#endif +#endif + printf("\n"); + printf( + " [-v] Verbose mode.\n"); + printf( + " [-h, --help] Print this message.\n"); + printf("\n"); +} + +void +daemonize(const char *path) +{ +#ifndef __MINGW32__ + /* Our process ID and Session ID */ + pid_t pid, sid; + + /* Fork off the parent process */ + pid = fork(); + if (pid < 0) { + exit(EXIT_FAILURE); + } + + /* If we got a good PID, then + * we can exit the parent process. */ + if (pid > 0) { + FILE *file = fopen(path, "w"); + if (file == NULL) { + FATAL("Invalid pid file\n"); + } + + fprintf(file, "%d", (int)pid); + fclose(file); + exit(EXIT_SUCCESS); + } + + /* Change the file mode mask */ + umask(0); + + /* Open any logs here */ + + /* Create a new SID for the child process */ + sid = setsid(); + if (sid < 0) { + /* Log the failure */ + exit(EXIT_FAILURE); + } + + /* Change the current working directory */ + if ((chdir("/")) < 0) { + /* Log the failure */ + exit(EXIT_FAILURE); + } + + /* Close out the standard file descriptors */ + close(STDIN_FILENO); + close(STDOUT_FILENO); + close(STDERR_FILENO); +#endif +} + +#ifdef HAVE_SETRLIMIT +int +set_nofile(int nofile) +{ + struct rlimit limit = { nofile, nofile }; /* set both soft and hard limit */ + + if (nofile <= 0) { + FATAL("nofile must be greater than 0\n"); + } + + if (setrlimit(RLIMIT_NOFILE, &limit) < 0) { + if (errno == EPERM) { + LOGE( + "insufficient permission to change NOFILE, not starting as root?"); + return -1; + } else if (errno == EINVAL) { + LOGE("invalid nofile, decrease nofile and try again"); + return -1; + } else { + LOGE("setrlimit failed: %s", strerror(errno)); + return -1; + } + } + + return 0; +} + +#endif diff --git a/shadowsocksr-libev/src/server/utils.h b/shadowsocksr-libev/src/server/utils.h new file mode 100644 index 000000000..0fb7f5a2c --- /dev/null +++ b/shadowsocksr-libev/src/server/utils.h @@ -0,0 +1,232 @@ +/* + * utils.h - Misc utilities + * + * Copyright (C) 2013 - 2016, Max Lv + * + * This file is part of the shadowsocks-libev. + * + * shadowsocks-libev is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * shadowsocks-libev 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with shadowsocks-libev; see the file COPYING. If not, see + * . + */ + +#if defined(USE_CRYPTO_OPENSSL) + +#include +#define USING_CRYPTO OPENSSL_VERSION_TEXT + +#elif defined(USE_CRYPTO_POLARSSL) +#include +#define USING_CRYPTO POLARSSL_VERSION_STRING_FULL + +#elif defined(USE_CRYPTO_MBEDTLS) +#include +#define USING_CRYPTO MBEDTLS_VERSION_STRING_FULL + +#endif + +#ifndef _UTILS_H +#define _UTILS_H + +#include +#include +#include +#include + +#define PORTSTRLEN 16 +#define SS_ADDRSTRLEN (INET6_ADDRSTRLEN + PORTSTRLEN + 1) + +#ifdef ANDROID + +#include + +#define USE_TTY() +#define USE_SYSLOG(ident) +#define LOGI(...) \ + ((void)__android_log_print(ANDROID_LOG_DEBUG, "shadowsocks", \ + __VA_ARGS__)) +#define LOGE(...) \ + ((void)__android_log_print(ANDROID_LOG_ERROR, "shadowsocks", \ + __VA_ARGS__)) + +#else + +#define STR(x) # x +#define TOSTR(x) STR(x) + +#ifdef LIB_ONLY + +extern FILE *logfile; + +#define TIME_FORMAT "%Y-%m-%d %H:%M:%S" + +#define USE_TTY() + +#define USE_SYSLOG(ident) + +#define USE_LOGFILE(ident) \ + do { \ + if (ident != NULL) { logfile = fopen(ident, "w+"); } } \ + while (0) + +#define CLOSE_LOGFILE \ + do { \ + if (logfile != NULL) { fclose(logfile); } } \ + while (0) + +#define LOGI(format, ...) \ + do { \ + if (logfile != NULL) { \ + time_t now = time(NULL); \ + char timestr[20]; \ + strftime(timestr, 20, TIME_FORMAT, localtime(&now)); \ + fprintf(logfile, " %s INFO: " format "\n", timestr, ## __VA_ARGS__); \ + fflush(logfile); } \ + } \ + while (0) + +#define LOGE(format, ...) \ + do { \ + if (logfile != NULL) { \ + time_t now = time(NULL); \ + char timestr[20]; \ + strftime(timestr, 20, TIME_FORMAT, localtime(&now)); \ + fprintf(logfile, " %s ERROR: " format "\n", timestr, \ + ## __VA_ARGS__); \ + fflush(logfile); } \ + } \ + while (0) + +#elif defined(_WIN32) + +#define TIME_FORMAT "%Y-%m-%d %H:%M:%S" + +#define USE_TTY() + +#define USE_SYSLOG(ident) + +#define LOGI(format, ...) \ + do { \ + time_t now = time(NULL); \ + char timestr[20]; \ + strftime(timestr, 20, TIME_FORMAT, localtime(&now)); \ + fprintf(stderr, " %s INFO: " format "\n", timestr, ## __VA_ARGS__); \ + fflush(stderr); } \ + while (0) + +#define LOGE(format, ...) \ + do { \ + time_t now = time(NULL); \ + char timestr[20]; \ + strftime(timestr, 20, TIME_FORMAT, localtime(&now)); \ + fprintf(stderr, " %s ERROR: " format "\n", timestr, ## __VA_ARGS__); \ + fflush(stderr); } \ + while (0) + +#else + +#include + +extern int use_tty; +#define USE_TTY() \ + do { \ + use_tty = isatty(STDERR_FILENO); \ + } while (0) \ + +#define HAS_SYSLOG +extern int use_syslog; + +#define TIME_FORMAT "%F %T" + +#define USE_SYSLOG(ident) \ + do { \ + use_syslog = 1; \ + openlog((ident), LOG_CONS | LOG_PID, 0); } \ + while (0) + +#define LOGI(format, ...) \ + do { \ + if (use_syslog) { \ + syslog(LOG_INFO, format, ## __VA_ARGS__); \ + } else { \ + time_t now = time(NULL); \ + char timestr[20]; \ + strftime(timestr, 20, TIME_FORMAT, localtime(&now)); \ + if (use_tty) { \ + fprintf(stderr, "\e[01;32m %s INFO: \e[0m" format "\n", timestr, \ + ## __VA_ARGS__); \ + } else { \ + fprintf(stderr, " %s INFO: " format "\n", timestr, \ + ## __VA_ARGS__); \ + } \ + } \ + } \ + while (0) + +#define LOGE(format, ...) \ + do { \ + if (use_syslog) { \ + syslog(LOG_ERR, format, ## __VA_ARGS__); \ + } else { \ + time_t now = time(NULL); \ + char timestr[20]; \ + strftime(timestr, 20, TIME_FORMAT, localtime(&now)); \ + if (use_tty) { \ + fprintf(stderr, "\e[01;35m %s ERROR: \e[0m" format "\n", timestr, \ + ## __VA_ARGS__); \ + } else { \ + fprintf(stderr, " %s ERROR: " format "\n", timestr, \ + ## __VA_ARGS__); \ + } \ + } } \ + while (0) + +#endif +/* _WIN32 */ + +#endif + +#ifdef __MINGW32__ + +#ifdef ERROR +#undef ERROR +#endif +#define ERROR(s) ss_error(s) + +#else + +void ERROR(const char *s); + +#endif + +char *ss_itoa(int i); +int ss_isnumeric(const char *s); +int run_as(const char *user); +void FATAL(const char *msg); +void usage(void); +void daemonize(const char *path); +char *ss_strndup(const char *s, size_t n); +#ifdef HAVE_SETRLIMIT +int set_nofile(int nofile); +#endif + +void *ss_malloc(size_t size); +void *ss_realloc(void *ptr, size_t new_size); + +#define ss_free(ptr) \ + do { \ + free(ptr); \ + ptr = NULL; \ + } while (0) + +#endif // _UTILS_H diff --git a/shadowsocksr-libev/src/server/verify.c b/shadowsocksr-libev/src/server/verify.c new file mode 100644 index 000000000..9e7393dd8 --- /dev/null +++ b/shadowsocksr-libev/src/server/verify.c @@ -0,0 +1,188 @@ + +#include "verify.h" + +static int verify_simple_pack_unit_size = 2000; + +typedef struct verify_simple_local_data { + char * recv_buffer; + int recv_buffer_size; +}verify_simple_local_data; + +void verify_simple_local_data_init(verify_simple_local_data* local) { + local->recv_buffer = (char*)malloc(16384); + local->recv_buffer_size = 0; +} + +obfs * verify_simple_new_obfs() { + obfs * self = new_obfs(); + self->l_data = malloc(sizeof(verify_simple_local_data)); + verify_simple_local_data_init((verify_simple_local_data*)self->l_data); + return self; +} + +void verify_simple_dispose(obfs *self) { + verify_simple_local_data *local = (verify_simple_local_data*)self->l_data; + if (local->recv_buffer != NULL) { + free(local->recv_buffer); + local->recv_buffer = NULL; + } + free(local); + self->l_data = NULL; + dispose_obfs(self); +} + +int verify_simple_pack_data(char *data, int datalength, char *outdata) { + unsigned char rand_len = (xorshift128plus() & 0xF) + 1; + int out_size = rand_len + datalength + 6; + outdata[0] = out_size >> 8; + outdata[1] = out_size; + outdata[2] = rand_len; + memmove(outdata + rand_len + 2, data, datalength); + fillcrc32((unsigned char *)outdata, out_size); + return out_size; +} + +int verify_simple_client_pre_encrypt(obfs *self, char **pplaindata, int datalength, size_t *capacity) { + char *plaindata = *pplaindata; + //verify_simple_local_data *local = (verify_simple_local_data*)self->l_data; + char * out_buffer = (char*)malloc(datalength * 2 + 32); + char * buffer = out_buffer; + char * data = plaindata; + int len = datalength; + int pack_len; + while ( len > verify_simple_pack_unit_size ) { + pack_len = verify_simple_pack_data(data, verify_simple_pack_unit_size, buffer); + buffer += pack_len; + data += verify_simple_pack_unit_size; + len -= verify_simple_pack_unit_size; + } + if (len > 0) { + pack_len = verify_simple_pack_data(data, len, buffer); + buffer += pack_len; + } + len = buffer - out_buffer; + if (*capacity < len) { + *pplaindata = (char*)realloc(*pplaindata, *capacity = len * 2); + plaindata = *pplaindata; + } + memmove(plaindata, out_buffer, len); + free(out_buffer); + return len; +} + +int verify_simple_client_post_decrypt(obfs *self, char **pplaindata, int datalength, size_t *capacity) { + char *plaindata = *pplaindata; + verify_simple_local_data *local = (verify_simple_local_data*)self->l_data; + uint8_t * recv_buffer = (uint8_t *)local->recv_buffer; + if (local->recv_buffer_size + datalength > 16384) + return -1; + memmove(recv_buffer + local->recv_buffer_size, plaindata, datalength); + local->recv_buffer_size += datalength; + + char * out_buffer = (char*)malloc(local->recv_buffer_size); + char * buffer = out_buffer; + while (local->recv_buffer_size > 2) { + int length = ((int)recv_buffer[0] << 8) | recv_buffer[1]; + if (length >= 8192 || length < 7) { + free(out_buffer); + local->recv_buffer_size = 0; + return -1; + } + if (length > local->recv_buffer_size) + break; + + int crc = crc32((unsigned char*)recv_buffer, length); + if (crc != -1) { + free(out_buffer); + local->recv_buffer_size = 0; + return -1; + } + int data_size = length - recv_buffer[2] - 6; + memmove(buffer, recv_buffer + 2 + recv_buffer[2], data_size); + buffer += data_size; + memmove(recv_buffer, recv_buffer + length, local->recv_buffer_size -= length); + } + int len = buffer - out_buffer; + if (*capacity < len) { + *pplaindata = (char*)realloc(*pplaindata, *capacity = len * 2); + plaindata = *pplaindata; + } + memmove(plaindata, out_buffer, len); + free(out_buffer); + return len; +} + +int verify_simple_server_pre_encrypt(obfs *self, char **pplaindata, int datalength, size_t *capacity) { + char *plaindata = *pplaindata; + //verify_simple_local_data *local = (verify_simple_local_data*)self->l_data; + char * out_buffer = (char*)malloc(datalength * 2 + 32); + char * buffer = out_buffer; + char * data = plaindata; + int len = datalength; + int pack_len; + while ( len > verify_simple_pack_unit_size ) { + pack_len = verify_simple_pack_data(data, verify_simple_pack_unit_size, buffer); + buffer += pack_len; + data += verify_simple_pack_unit_size; + len -= verify_simple_pack_unit_size; + } + if (len > 0) { + pack_len = verify_simple_pack_data(data, len, buffer); + buffer += pack_len; + } + len = buffer - out_buffer; + if (*capacity < len) { + *pplaindata = (char*)realloc(*pplaindata, *capacity = len * 2); + plaindata = *pplaindata; + } + memmove(plaindata, out_buffer, len); + free(out_buffer); + return len; +} + +int verify_simple_server_post_decrypt(obfs *self, char **pplaindata, int datalength, size_t *capacity) { + char *plaindata = *pplaindata; + verify_simple_local_data *local = (verify_simple_local_data*)self->l_data; + uint8_t * recv_buffer = (uint8_t *)local->recv_buffer; + if (local->recv_buffer_size + datalength > 16384) + { + LOGE("verify_simple: wrong buf length %d", local->recv_buffer_size + datalength); + return -1; + } + memmove(recv_buffer + local->recv_buffer_size, plaindata, datalength); + local->recv_buffer_size += datalength; + + char * out_buffer = (char*)malloc(local->recv_buffer_size); + char * buffer = out_buffer; + while (local->recv_buffer_size > 2) { + int length = ((int)recv_buffer[0] << 8) | recv_buffer[1]; + if (length >= 8192 || length < 7) { + free(out_buffer); + local->recv_buffer_size = 0; + LOGE("verify_simple: wrong length %d", length); + return -1; + } + if (length > local->recv_buffer_size) + break; + + int crc = crc32((unsigned char*)recv_buffer, length); + if (crc != -1) { + free(out_buffer); + local->recv_buffer_size = 0; + LOGE("verify_simple: wrong crc"); + return -1; + } + int data_size = length - recv_buffer[2] - 6; + memmove(buffer, recv_buffer + 2 + recv_buffer[2], data_size); + buffer += data_size; + memmove(recv_buffer, recv_buffer + length, local->recv_buffer_size -= length); + } + int len = buffer - out_buffer; + if (*capacity < len) { + *pplaindata = (char*)realloc(*pplaindata, *capacity = len * 2); + plaindata = *pplaindata; + } + memmove(plaindata, out_buffer, len); + free(out_buffer); + return len; +} diff --git a/shadowsocksr-libev/src/server/verify.h b/shadowsocksr-libev/src/server/verify.h new file mode 100644 index 000000000..57c6ff997 --- /dev/null +++ b/shadowsocksr-libev/src/server/verify.h @@ -0,0 +1,19 @@ +/* + * verify.h - Define shadowsocksR server's buffers and callbacks + * + * Copyright (C) 2015 - 2016, Break Wa11 + */ + +#ifndef _VERIFY_H +#define _VERIFY_H + +obfs * verify_simple_new_obfs(); +void verify_simple_dispose(obfs *self); + +int verify_simple_client_pre_encrypt(obfs *self, char **pplaindata, int datalength, size_t* capacity); +int verify_simple_client_post_decrypt(obfs *self, char **pplaindata, int datalength, size_t* capacity); + +int verify_simple_server_pre_encrypt(obfs *self, char **pplaindata, int datalength, size_t* capacity); +int verify_simple_server_post_decrypt(obfs *self, char **pplaindata, int datalength, size_t* capacity); + +#endif // _VERIFY_H diff --git a/simple-obfs/Makefile b/simple-obfs/Makefile new file mode 100644 index 000000000..4c05fed38 --- /dev/null +++ b/simple-obfs/Makefile @@ -0,0 +1,71 @@ +# SPDX-License-Identifier: GPL-3.0-or-later +# +# Copyright (C) 2017-2019 Jian Chang +# Copyright (C) 2021 ImmortalWrt.org + +include $(TOPDIR)/rules.mk + +PKG_NAME:=simple-obfs +PKG_VERSION:=0.0.5 +PKG_RELEASE:=1 + +PKG_SOURCE_PROTO:=git +PKG_SOURCE_URL:=https://github.com/shadowsocks/simple-obfs.git +PKG_SOURCE_DATE:=2019-08-17 +PKG_SOURCE_VERSION:=486bebd9208539058e57e23a12f23103016e09b4 +PKG_MIRROR_HASH:=bc97eba511b86a089ab4bcf0ac78d9e4a39c59046d5cde77b79a118245daa0ba + +PKG_LICENSE:=GPL-3.0-or-later +PKG_LICENSE_FILES:=LICENSE +PKG_MAINTAINER:=Jian Chang + +PKG_BUILD_DEPENDS:=libev +PKG_FIXUP:=autoreconf +PKG_BUILD_PARALLEL:=1 +PKG_INSTALL:=1 + +include $(INCLUDE_DIR)/package.mk + +define Package/simple-obfs/template + SECTION:=net + CATEGORY:=Network + TITLE:=A simple obfuscating tool + URL:=https://github.com/shadowsocks/simple-obfs + DEPENDS:=+libpthread +libev +endef + +define Package/simple-obfs-client + $(call Package/simple-obfs/template) + TITLE+= (client) + PROVIDES:=simple-obfs +endef + +define Package/simple-obfs-server + $(call Package/simple-obfs/template) + TITLE+= (server) +endef + +define Package/simple-obfs/description + Simple-obfs is a simple obfusacting tool, designed as plugin server of shadowsocks. +endef + +Package/simple-obfs-client/description = $(Package/simple-obfs/description) +Package/simple-obfs-server/description = $(Package/simple-obfs/description) + +CONFIGURE_ARGS += \ + --disable-ssp \ + --disable-documentation \ + --disable-assert + +define Package/simple-obfs-client/install + $(INSTALL_DIR) $(1)/usr/bin + $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/obfs-local $(1)/usr/bin/ +endef + +define Package/simple-obfs-server/install + $(INSTALL_DIR) $(1)/usr/bin + $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/obfs-server $(1)/usr/bin/ +endef + +$(eval $(call BuildPackage,simple-obfs-client)) +$(eval $(call BuildPackage,simple-obfs-server)) diff --git a/sing-box/Makefile b/sing-box/Makefile new file mode 100644 index 000000000..399bb84aa --- /dev/null +++ b/sing-box/Makefile @@ -0,0 +1,170 @@ +# SPDX-License-Identifier: GPL-3.0-only +# +# Copyright (C) 2022-2023 ImmortalWrt.org + +include $(TOPDIR)/rules.mk + +PKG_NAME:=sing-box +PKG_VERSION:=1.12.13 +PKG_RELEASE:=1 + +PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz +PKG_SOURCE_URL:=https://codeload.github.com/SagerNet/sing-box/tar.gz/v$(PKG_VERSION)? +PKG_HASH:=e8bc2c059757af705f8e96c1909e2693f79a4c5c573464529af95c5c93046f1b + +PKG_LICENSE:=GPL-3.0-or-later +PKG_LICENSE_FILES:=LICENSE +PKG_MAINTAINER:=Tianling Shen + +PKG_CONFIG_DEPENDS:= \ + CONFIG_SING_BOX_TINY_BUILD_ACME \ + CONFIG_SING_BOX_TINY_BUILD_CLASH_API \ + CONFIG_SING_BOX_TINY_BUILD_DHCP \ + CONFIG_SING_BOX_TINY_BUILD_EMBEDDED_TOR \ + CONFIG_SING_BOX_TINY_BUILD_GRPC \ + CONFIG_SING_BOX_TINY_BUILD_GVISOR \ + CONFIG_SING_BOX_TINY_BUILD_QUIC \ + CONFIG_SING_BOX_TINY_BUILD_TAILSCALE \ + CONFIG_SING_BOX_TINY_BUILD_UTLS \ + CONFIG_SING_BOX_TINY_BUILD_V2RAY_API \ + CONFIG_SING_BOX_TINY_BUILD_WIREGUARD + +PKG_BUILD_DEPENDS:=golang/host +PKG_BUILD_PARALLEL:=1 +PKG_BUILD_FLAGS:=no-mips16 + +GO_PKG:=github.com/sagernet/sing-box +GO_PKG_BUILD_PKG:=$(GO_PKG)/cmd/sing-box +GO_PKG_LDFLAGS_X:=$(GO_PKG)/constant.Version=$(PKG_VERSION) + +include $(INCLUDE_DIR)/package.mk +include $(TOPDIR)/feeds/packages/lang/golang/golang-package.mk + +define Package/sing-box/Default + SECTION:=net + CATEGORY:=Network + SUBMENU:=Web Servers/Proxies + TITLE:=The universal proxy platform + URL:=https://sing-box.sagernet.org/ + DEPENDS:=+ca-bundle +kmod-inet-diag + USERID:=sing-box=5566:sing-box=5566 +endef + +define Package/sing-box + $(call Package/sing-box/Default) + TITLE+= (full version) + DEPENDS+= +kmod-netlink-diag +kmod-tun + CONFLICTS:=sing-box-tiny + VARIANT:=full + DEFAULT_VARIANT:=1 +endef + +define Package/sing-box/description + Sing-box is a universal proxy platform which supports hysteria, SOCKS, Shadowsocks, + ShadowTLS, Tor, trojan, VLess, VMess, WireGuard and so on. +endef + +define Package/sing-box/config + depends on $(subst @,,$(GO_ARCH_DEPENDS)) +endef + +define Package/sing-box-tiny + $(call Package/sing-box/Default) + TITLE+= (tiny/custom version) + PROVIDES:=sing-box + VARIANT:=tiny +endef + +Package/sing-box-tiny/description:=$(Package/sing-box/description) + +define Package/sing-box-tiny/config + $(call Package/sing-box/config) + + if PACKAGE_sing-box-tiny + config SING_BOX_TINY_BUILD_ACME + bool "Build with ACME TLS certificate issuer support" + + config SING_BOX_TINY_BUILD_CLASH_API + bool "Build with Clash API support" + default y + + config SING_BOX_TINY_BUILD_DHCP + bool "Build with DHCP support" + + config SING_BOX_TINY_BUILD_EMBEDDED_TOR + bool "Build with embedded Tor support" + depends on BROKEN + + config SING_BOX_TINY_BUILD_GRPC + bool "Build with standard gPRC support" + help + Standard gRPC has good compatibility but poor performance. + + config SING_BOX_TINY_BUILD_GVISOR + bool "Build with gVisor support" + default y if aarch64||arm||i386||x86_64 + + config SING_BOX_TINY_BUILD_QUIC + bool "Build with QUIC support" + default y + help + Required by HTTP3 DNS transports, Naive inbound, + Hysteria inbound / outbound, and v2ray QUIC transport. + + config SING_BOX_TINY_BUILD_TAILSCALE + bool "Build with Tailscale support" + + config SING_BOX_TINY_BUILD_UTLS + bool "Build with uTLS support" + default y + + config SING_BOX_TINY_BUILD_V2RAY_API + bool "Build with V2Ray API support" + + config SING_BOX_TINY_BUILD_WIREGUARD + bool "Build with WireGuard support" + default y if aarch64||arm||i386||x86_64 + endif +endef + +ifeq ($(BUILD_VARIANT),full) + GO_PKG_TAGS:=with_acme,with_clash_api,with_dhcp,with_gvisor,with_quic,with_tailscale,with_utls,with_wireguard +else + GO_PKG_TAGS:=$(subst $(space),$(comma),$(strip \ + $(if $(CONFIG_SING_BOX_TINY_BUILD_ACME),with_acme) \ + $(if $(CONFIG_SING_BOX_TINY_BUILD_CLASH_API),with_clash_api) \ + $(if $(CONFIG_SING_BOX_TINY_BUILD_DHCP),with_dhcp) \ + $(if $(CONFIG_SING_BOX_TINY_BUILD_EMBEDDED_TOR),with_embedded_tor) \ + $(if $(CONFIG_SING_BOX_TINY_BUILD_GRPC),with_grpc) \ + $(if $(CONFIG_SING_BOX_TINY_BUILD_GVISOR),with_gvisor) \ + $(if $(CONFIG_SING_BOX_TINY_BUILD_QUIC),with_quic) \ + $(if $(CONFIG_SING_BOX_TINY_BUILD_TAILSCALE),with_tailscale) \ + $(if $(CONFIG_SING_BOX_TINY_BUILD_UTLS),with_utls) \ + $(if $(CONFIG_SING_BOX_TINY_BUILD_V2RAY_API),with_v2ray_api) \ + $(if $(CONFIG_SING_BOX_TINY_BUILD_WIREGUARD),with_wireguard) \ + )) +endif + +define Package/sing-box/conffiles +/etc/config/sing-box +/etc/sing-box/ +endef + +Package/sing-box-tiny/conffiles=$(Package/sing-box/conffiles) + +define Package/sing-box/install + $(call GoPackage/Package/Install/Bin,$(1)) + + $(INSTALL_DIR) $(1)/etc/sing-box + $(INSTALL_DATA) $(PKG_BUILD_DIR)/release/config/config.json $(1)/etc/sing-box + + $(INSTALL_DIR) $(1)/etc/config/ + $(INSTALL_CONF) ./files/sing-box.conf $(1)/etc/config/sing-box + $(INSTALL_DIR) $(1)/etc/init.d/ + $(INSTALL_BIN) ./files/sing-box.init $(1)/etc/init.d/sing-box +endef + +Package/sing-box-tiny/install=$(Package/sing-box/install) + +$(eval $(call BuildPackage,sing-box)) +$(eval $(call BuildPackage,sing-box-tiny)) diff --git a/sing-box/files/sing-box.conf b/sing-box/files/sing-box.conf new file mode 100644 index 000000000..a6489b07d --- /dev/null +++ b/sing-box/files/sing-box.conf @@ -0,0 +1,10 @@ + +config sing-box 'main' + option enabled '0' + option user 'root' + option conffile '/etc/sing-box/config.json' + option workdir '/usr/share/sing-box' +# list ifaces 'wan' +# list ifaces 'wan6' + option log_stderr '1' + option log_stdout '0' diff --git a/sing-box/files/sing-box.init b/sing-box/files/sing-box.init new file mode 100644 index 000000000..ecc22b587 --- /dev/null +++ b/sing-box/files/sing-box.init @@ -0,0 +1,55 @@ +#!/bin/sh /etc/rc.common + +USE_PROCD=1 +START=99 + +script=$(readlink "$initscript") +NAME="$(basename ${script:-$initscript})" +PROG="/usr/bin/sing-box" + +start_service() { + config_load "$NAME" + + local enabled user group conffile workdir ifaces + local log_stdout log_stderr + config_get_bool enabled "main" "enabled" "0" + [ "$enabled" -eq "1" ] || return 0 + + config_get user "main" "user" "root" + config_get conffile "main" "conffile" + config_get ifaces "main" "ifaces" + config_get workdir "main" "workdir" "/usr/share/sing-box" + config_get_bool log_stdout "main" "log_stdout" "0" + config_get_bool log_stderr "main" "log_stderr" "1" + + mkdir -p "$workdir" + local group="$(id -ng $user)" + chown $user:$group "$workdir" + + procd_open_instance "$NAME.main" + procd_set_param command "$PROG" run -c "$conffile" -D "$workdir" + + # Use root user if you want to use the TUN mode. + procd_set_param user "$user" + procd_set_param file "$conffile" + [ -z "$ifaces" ] || procd_set_param netdev $ifaces + procd_set_param stdout "$log_stdout" + procd_set_param stderr "$log_stderr" + procd_set_param limits core="unlimited" + procd_set_param limits nofile="1000000 1000000" + procd_set_param respawn + + procd_close_instance +} + +service_triggers() { + local ifaces + config_load "$NAME" + config_get ifaces "main" "ifaces" + procd_open_trigger + for iface in $ifaces; do + procd_add_interface_trigger "interface.*.up" $iface /etc/init.d/$NAME restart + done + procd_close_trigger + procd_add_reload_trigger "$NAME" +} diff --git a/smartdns/Makefile b/smartdns/Makefile new file mode 100644 index 000000000..649e500b7 --- /dev/null +++ b/smartdns/Makefile @@ -0,0 +1,140 @@ +# SPDX-License-Identifier: GPL-3.0-or-later +# +# Copyright (c) 2018-2023 Nick Peng (pymumu@gmail.com) + +include $(TOPDIR)/rules.mk + +PKG_NAME:=smartdns +PKG_VERSION:=47.1 +PKG_RELEASE:=1 + +PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz +PKG_SOURCE_URL:=https://codeload.github.com/pymumu/smartdns/tar.gz/Release$(PKG_VERSION)? +PKG_HASH:=0a143ee81ccb7a31b7b7b0c29d6a6ee41a54331da75477719925592af124ec97 +PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-Release$(PKG_VERSION) + +PKG_MAINTAINER:=Nick Peng +PKG_LICENSE:=GPL-3.0-or-later +PKG_LICENSE_FILES:=LICENSE + +PKG_CONFIG_DEPENDS:=CONFIG_PACKAGE_smartdns-ui +PKG_BUILD_DEPENDS:=PACKAGE_smartdns-ui:node/host PACKAGE_smartdns-ui:rust-bindgen/host +PKG_BUILD_PARALLEL:=1 + +RUST_PKG_FEATURES:=build-release +RUST_PKG_LOCKED:=0 + +include $(INCLUDE_DIR)/package.mk +include $(TOPDIR)/feeds/packages/lang/rust/rust-package.mk + +MAKE_PATH:=src +MAKE_VARS+= VER=$(PKG_VERSION) + +define Package/smartdns/Default + SECTION:=net + CATEGORY:=Network + SUBMENU:=IP Addresses and Names + TITLE:=smartdns + URL:=https://www.github.com/pymumu/smartdns/ +endef + +define Package/smartdns + $(call Package/smartdns/Default) + TITLE+= server + DEPENDS:=+i386:libatomic +libopenssl +endef + +define Package/smartdns/description +SmartDNS is a local DNS server which accepts DNS query requests from local network clients, +gets DNS query results from multiple upstream DNS servers concurrently, and returns the fastest IP to clients. +Unlike dnsmasq's all-servers, smartdns returns the fastest IP, and encrypt DNS queries with DoT or DoH. +endef + +define Package/smartdns-ui + $(call Package/smartdns/Default) + TITLE+= dashboard + DEPENDS:=+smartdns $(RUST_ARCH_DEPENDS) @!(TARGET_x86_geode||TARGET_x86_legacy) +endef + +define Package/smartdns-ui/description +A dashboard ui for smartdns server. +endef + +define Package/smartdns/conffiles +/etc/config/smartdns +/etc/smartdns/address.conf +/etc/smartdns/blacklist-ip.conf +/etc/smartdns/custom.conf +/etc/smartdns/domain-block.list +/etc/smartdns/domain-forwarding.list +endef + +define Download/smartdns-webui + PROTO:=git + URL:=https://github.com/pymumu/smartdns-webui.git + SOURCE_DATE:=2025-09-18 + SOURCE_VERSION:=c322303eac2ebee389f4a72a002163552e552f74 + MIRROR_HASH:=b239f3d994e05ad08356bf1be629cd84c2bfc6706997aafea881284cacc29545 + SUBDIR:=smartdns-webui-$$$$(subst -,.,$$$$(SOURCE_DATE))~$$$$(call version_abbrev,$$$$(SOURCE_VERSION)) + FILE:=$$(SUBDIR).tar.zst +endef + +define Build/Prepare + $(call Build/Prepare/Default) + +ifneq ($(CONFIG_PACKAGE_smartdns-ui),) + $(eval $(call Download,smartdns-webui)) + $(eval $(Download/smartdns-webui)) + mkdir -p $(PKG_BUILD_DIR)/smartdns-webui + zstdcat $(DL_DIR)/$(FILE) | tar -C $(PKG_BUILD_DIR)/smartdns-webui $(TAR_OPTIONS) --strip-components=1 +endif +endef + +define Build/Compile + $(call Build/Compile/Default) + +ifneq ($(CONFIG_PACKAGE_smartdns-ui),) + ( \ + pushd $(PKG_BUILD_DIR) ; \ + pushd smartdns-webui ; \ + npm install ; \ + npm run build ; \ + popd ; \ + pushd plugin/smartdns-ui ; \ + $(CARGO_PKG_CONFIG_VARS) \ + MAKEFLAGS="$(PKG_JOBS)" \ + TARGET_CFLAGS="$(filter-out -O%,$(TARGET_CFLAGS)) $(RUSTC_CFLAGS)" \ + BINDGEN_EXTRA_CLANG_ARGS="--sysroot=$(TOOLCHAIN_ROOT_DIR)" \ + cargo build -v --profile $(CARGO_PKG_PROFILE) \ + $(if $(strip $(RUST_PKG_FEATURES)),--features "$(strip $(RUST_PKG_FEATURES))") \ + $(if $(filter --jobserver%,$(PKG_JOBS)),,-j1) \ + $(CARGO_PKG_ARGS) ; \ + popd ; \ + popd ; \ + ) +endif +endef + +define Package/smartdns/install + $(INSTALL_DIR) $(1)/usr/sbin $(1)/etc/config $(1)/etc/init.d + $(INSTALL_DIR) $(1)/etc/smartdns $(1)/etc/smartdns/domain-set $(1)/etc/smartdns/conf.d/ + $(INSTALL_DIR) $(1)/etc/smartdns/ip-set $(1)/etc/smartdns/download + $(INSTALL_BIN) $(PKG_BUILD_DIR)/src/smartdns $(1)/usr/sbin/smartdns + $(INSTALL_BIN) $(PKG_BUILD_DIR)/package/openwrt/files/etc/init.d/smartdns $(1)/etc/init.d/smartdns + $(INSTALL_CONF) $(PKG_BUILD_DIR)/package/openwrt/address.conf $(1)/etc/smartdns/address.conf + $(INSTALL_CONF) $(PKG_BUILD_DIR)/package/openwrt/blacklist-ip.conf $(1)/etc/smartdns/blacklist-ip.conf + $(INSTALL_CONF) $(PKG_BUILD_DIR)/package/openwrt/custom.conf $(1)/etc/smartdns/custom.conf + $(INSTALL_CONF) $(PKG_BUILD_DIR)/package/openwrt/domain-block.list $(1)/etc/smartdns/domain-block.list + $(INSTALL_CONF) $(PKG_BUILD_DIR)/package/openwrt/domain-forwarding.list $(1)/etc/smartdns/domain-forwarding.list + $(INSTALL_CONF) $(PKG_BUILD_DIR)/package/openwrt/files/etc/config/smartdns $(1)/etc/config/smartdns +endef + +define Package/smartdns-ui/install + $(INSTALL_DIR) $(1)/usr/lib + $(INSTALL_DIR) $(1)/usr/share/smartdns + $(CP) $(PKG_BUILD_DIR)/plugin/smartdns-ui/target/$(RUSTC_TARGET_ARCH)/$(CARGO_PKG_PROFILE)/libsmartdns_ui.so $(1)/usr/lib/smartdns_ui.so + $(CP) $(PKG_BUILD_DIR)/smartdns-webui/out $(1)/usr/share/smartdns/wwwroot +endef + +$(eval $(call BuildPackage,smartdns)) +$(eval $(call BuildPackage,smartdns-ui)) diff --git a/ssocks/Makefile b/ssocks/Makefile new file mode 100644 index 000000000..8439e4f65 --- /dev/null +++ b/ssocks/Makefile @@ -0,0 +1,79 @@ +# +# Copyright (C) 2017-2018 Jian Chang +# +# Copyright (C) 2021 ImmortalWrt +# +# +# This is free software, licensed under the GNU General Public License v3. +# See /LICENSE for more information. +# + +include $(TOPDIR)/rules.mk + +PKG_NAME:=ssocks +PKG_VERSION:=0.0.14 +PKG_RELEASE:=3 + +PKG_SOURCE_PROTO:=git +PKG_SOURCE_URL:=https://github.com/tostercx/ssocks.git +PKG_SOURCE_DATE:=2020-07-09 +PKG_SOURCE_VERSION:=c2024789c1ee076d171fd6061d7c133302216ea7 +PKG_MIRROR_HASH:=2e9d1a2e6b47303389410952867d204e03054763d024a22a838a9707c2daca03 + +PKG_LICENSE:=MIT +PKG_LICENSE_FILES:=LICENSE +PKG_MAINTAINER:=Tianling Shen + +PKG_BUILD_PARALLEL:=1 + +include $(INCLUDE_DIR)/package.mk +include $(INCLUDE_DIR)/cmake.mk + +define Package/ssocks/template + SECTION:=net + CATEGORY:=Network + TITLE:=sSocks + URL:=https://github.com/tostercx/ssocks + DEPENDS:=+libopenssl +endef + +define Package/ssocks + $(call Package/ssocks/template) + TITLE+= Relay +endef + +define Package/ssocksd + $(call Package/ssocks/template) + TITLE+= Server +endef + +define Package/ssocks/description/template + sSocks is a package which contains: a socks5 server implements RFC 1928 (SOCKS V5) and + RFC 1929 (Authentication for SOCKS V5), a reverse socks server and client, a netcat like tool + and a socks5 relay. +endef + +Package/ssocks/description = $(Package/ssocks/description/template) +Package/ssocksd/description = $(Package/ssocks/description/template) + +define Build/Install + true +endef + +define Package/ssocks/install/template + $(INSTALL_DIR) $(1)/usr/bin + $(INSTALL_BIN) $(PKG_BUILD_DIR)/$(2) $(1)/usr/bin/ +endef + +define Package/ssocks/install + $(call Package/ssocks/install/template,$(1),ssocks) +endef + +define Package/ssocksd/install + $(call Package/ssocks/install/template,$(1),ssocksd) +endef + +$(eval $(call BuildPackage,ssocks)) +$(eval $(call BuildPackage,ssocksd)) + + diff --git a/ssocks/patches/001-fix.patch b/ssocks/patches/001-fix.patch new file mode 100644 index 000000000..6a19295dd --- /dev/null +++ b/ssocks/patches/001-fix.patch @@ -0,0 +1,15 @@ +--- a/CMakeLists.txt 2020-09-07 14:23:49.000000000 +0800 ++++ b/CMakeLists.txt 2020-09-07 14:23:24.000000000 +0800 +@@ -11,7 +11,12 @@ + endif(WIN32) + + add_executable(ssocks src/ssocks.c ${AUX}) ++INSTALL(TARGETS ssocks RUNTIME DESTINATION bin) + add_executable(ssocksd src/ssocksd.c ${AUX}) ++INSTALL(TARGETS ssocksd RUNTIME DESTINATION bin) + add_executable(nsocks src/nsocks.c ${AUX}) ++INSTALL(TARGETS nsocks RUNTIME DESTINATION bin) + add_executable(rcsocks src/rcsocks.c ${AUX}) ++INSTALL(TARGETS rcsocks RUNTIME DESTINATION bin) + add_executable(rssocks src/rssocks.c ${AUX}) ++INSTALL(TARGETS rssocks RUNTIME DESTINATION bin) diff --git a/ssocks/patches/002-gcc10.patch b/ssocks/patches/002-gcc10.patch new file mode 100644 index 000000000..f25c19db1 --- /dev/null +++ b/ssocks/patches/002-gcc10.patch @@ -0,0 +1,11 @@ +--- a/src/configd-util.h 2020-07-09 05:30:54.000000000 +0800 ++++ b/src/configd-util.h 2020-09-08 19:35:11.000000000 +0800 +@@ -33,7 +33,7 @@ + + + +-struct globalArgsServer_t { ++static struct globalArgsServer_t { + char fileauth[255]; // -a option + char fileconfig[255]; // -f option + char filelog[255]; // -l option diff --git a/tcping/Makefile b/tcping/Makefile new file mode 100644 index 000000000..f544a3b6b --- /dev/null +++ b/tcping/Makefile @@ -0,0 +1,38 @@ +# SPDX-License-Identifier: GPL-3.0-only +# +# Copyright (C) 2021 ImmortalWrt.org + +include $(TOPDIR)/rules.mk + +PKG_NAME:=tcping +PKG_VERSION:=0.3 +PKG_RELEASE:=2 + +PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz +PKG_SOURCE_URL:=https://codeload.github.com/Mattraks/tcping/tar.gz/$(PKG_VERSION)? +PKG_HASH:=c703481d1751adf051dd3391c4dccdadc6dfca7484e636222b392e1213312e02 + +PKG_LICENSE:=GPL-2.0-only +PKG_LICENSE_FILE:=license.txt + +PKG_BUILD_PARALLEL:=1 + +include $(INCLUDE_DIR)/package.mk + +define Package/tcping + SECTION:=net + CATEGORY:=Network + TITLE:=tcping measures the latency of a tcp-connection (IPv4/IPv6 support) + URL:=https://github.com/jlyo/tcping +endef + +define Build/Compile + $(MAKE) -C $(PKG_BUILD_DIR) CC="$(TARGET_CC)" CFLAGS="$(TARGET_CFLAGS) -Wall" LDFLAGS="$(TARGET_LDFLAGS)" +endef + +define Package/tcping/install + $(INSTALL_DIR) $(1)/usr/sbin + $(INSTALL_BIN) $(PKG_BUILD_DIR)/tcping $(1)/usr/sbin +endef + +$(eval $(call BuildPackage,tcping)) diff --git a/trojan-go/Makefile b/trojan-go/Makefile new file mode 100644 index 000000000..b8fd4b5ad --- /dev/null +++ b/trojan-go/Makefile @@ -0,0 +1,49 @@ +# SPDX-License-Identifier: GPL-3.0-only +# +# Copyright (C) 2021 ImmortalWrt.org + +include $(TOPDIR)/rules.mk + +PKG_NAME:=trojan-go +PKG_VERSION:=0.10.6 +PKG_RELEASE:=1 + +PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz +PKG_SOURCE_URL:=https://codeload.github.com/p4gefau1t/trojan-go/tar.gz/v$(PKG_VERSION)? +PKG_HASH:=925f02647dda944813f1eab0b71eac98b83eb5964ef5a6f63e89bc7eb4486c1f + +PKG_LICENSE:=GPL-3.0-only +PKG_LICENSE_FILES:=LICENSE +PKG_MAINTAINER:=Tianling Shen + +PKG_BUILD_DEPENDS:=golang/host +PKG_BUILD_PARALLEL:=1 +PKG_USE_MIPS16:=0 +PKG_BUILD_FLAGS:=no-mips16 + +GO_PKG:=github.com/p4gefau1t/trojan-go +GO_PKG_BUILD_PKG:=$(GO_PKG) +GO_PKG_LDFLAGS_X:= \ + $(GO_PKG)/constant.Version=$(PKG_VERSION) \ + $(GO_PKG)/constant.Commit=v$(PKG_VERSION) +GO_PKG_TAGS:=full + +include $(INCLUDE_DIR)/package.mk +include $(TOPDIR)/feeds/packages/lang/golang/golang-package.mk + +define Package/trojan-go + SECTION:=net + CATEGORY:=Network + SUBMENU:=Web Servers/Proxies + TITLE:=A Trojan proxy written in Go + DEPENDS:=$(GO_ARCH_DEPENDS) +ca-bundle + URL:=https://p4gefau1t.github.io/trojan-go/ +endef + +define Package/trojan-go/description + Trojan features multiple protocols over TLS to avoid both + active/passive detections and ISP QoS limitations. +endef + +$(eval $(call GoBinPackage,trojan-go)) +$(eval $(call BuildPackage,trojan-go)) diff --git a/trojan-plus/Makefile b/trojan-plus/Makefile new file mode 100644 index 000000000..0206b6982 --- /dev/null +++ b/trojan-plus/Makefile @@ -0,0 +1,61 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=trojan-plus +PKG_VERSION:=10.0.3 +PKG_RELEASE:=3 + +PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz +PKG_SOURCE_URL:=https://codeload.github.com/peter-tank/trojan-plus/tar.gz/v$(PKG_VERSION)? +PKG_HASH:=0240f5b4d2a37074ab33b414a8b60d5f5b4778bd226586a2bd9a5c87a12896ed + +PKG_BUILD_PARALLEL:=1 +PKG_BUILD_DEPENDS:=openssl + +PKG_LICENSE:=GPL-3.0 +PKG_LICENSE_FILE:=LICENSE +PKG_MAINTAINER:=Trojan-Plus-Group + +include $(INCLUDE_DIR)/package.mk +include $(INCLUDE_DIR)/cmake.mk + +TARGET_CXXFLAGS += -Wall -Wextra +TARGET_CXXFLAGS += $(FPIC) + +# LTO +TARGET_CXXFLAGS += -flto +TARGET_LDFLAGS += -flto + +# CXX standard +TARGET_CXXFLAGS += -std=c++11 +TARGET_CXXFLAGS := $(filter-out -O%,$(TARGET_CXXFLAGS)) -O3 +TARGET_CXXFLAGS += -ffunction-sections -fdata-sections +TARGET_LDFLAGS += -Wl,--gc-sections + +CMAKE_OPTIONS += \ + -DENABLE_MYSQL=OFF \ + -DENABLE_NAT=ON \ + -DENABLE_REUSE_PORT=ON \ + -DENABLE_SSL_KEYLOG=ON \ + -DENABLE_TLS13_CIPHERSUITES=ON \ + -DFORCE_TCP_FASTOPEN=OFF \ + -DSYSTEMD_SERVICE=OFF \ + -DOPENSSL_USE_STATIC_LIBS=FALSE \ + -DBoost_DEBUG=ON \ + -DBoost_NO_BOOST_CMAKE=ON + +define Package/trojan-plus + SECTION:=net + CATEGORY:=Network + TITLE:=An unidentifiable mechanism that helps you bypass GFW. It's compatible with original trojan with experimental features. + URL:=https://github.com/Trojan-Plus-Group/trojan-plus + DEPENDS:= \ + +libpthread +libstdcpp +libopenssl \ + +boost +boost-system +boost-program_options +endef + +define Package/trojan-plus/install + $(INSTALL_DIR) $(1)/usr/sbin + $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/trojan $(1)/usr/sbin/trojan-plus +endef + +$(eval $(call BuildPackage,trojan-plus)) diff --git a/trojan/Makefile b/trojan/Makefile new file mode 100644 index 000000000..b75a18a5c --- /dev/null +++ b/trojan/Makefile @@ -0,0 +1,68 @@ +# +# Copyright (C) 2018-2019 wongsyrone +# +# This is free software, licensed under the GNU General Public License v3. +# See /LICENSE for more information. +# +include $(TOPDIR)/rules.mk + +PKG_NAME:=trojan +PKG_VERSION:=1.16.0 +PKG_RELEASE:=2 + +PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz +PKG_SOURCE_URL:=https://codeload.github.com/trojan-gfw/trojan/tar.gz/v$(PKG_VERSION)? +PKG_HASH:=86cdb2685bb03a63b62ce06545c41189952f1ec4a0cd9147450312ed70956cbc + +PKG_BUILD_PARALLEL:=1 +PKG_BUILD_DEPENDS:=openssl + +PKG_LICENSE:=GPL-3.0 +PKG_LICENSE_FILE:=LICENSE +PKG_MAINTAINER:=GreaterFire + +include $(INCLUDE_DIR)/package.mk +include $(INCLUDE_DIR)/cmake.mk + +TARGET_CXXFLAGS += -Wall -Wextra +TARGET_CXXFLAGS += $(FPIC) + +# LTO +TARGET_CXXFLAGS += -flto +TARGET_LDFLAGS += -flto + +# CXX standard +TARGET_CXXFLAGS += -std=c++11 +TARGET_CXXFLAGS := $(filter-out -O%,$(TARGET_CXXFLAGS)) -O3 +TARGET_CXXFLAGS += -ffunction-sections -fdata-sections +TARGET_LDFLAGS += -Wl,--gc-sections + +CMAKE_OPTIONS += \ + -DENABLE_MYSQL=OFF \ + -DENABLE_NAT=ON \ + -DENABLE_REUSE_PORT=ON \ + -DENABLE_SSL_KEYLOG=ON \ + -DENABLE_TLS13_CIPHERSUITES=ON \ + -DFORCE_TCP_FASTOPEN=OFF \ + -DSYSTEMD_SERVICE=OFF \ + -DOPENSSL_USE_STATIC_LIBS=FALSE \ + -DBoost_DEBUG=ON \ + -DBoost_NO_BOOST_CMAKE=ON + +define Package/trojan + SECTION:=net + CATEGORY:=Network + SUBMENU:=Web Servers/Proxies + TITLE:=An unidentifiable mechanism that helps you bypass GFW + URL:=https://github.com/trojan-gfw/trojan + DEPENDS:= \ + +libpthread +libstdcpp +libopenssl \ + +boost +boost-system +boost-program_options +boost-date_time +endef + +define Package/trojan/install + $(INSTALL_DIR) $(1)/usr/sbin + $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/trojan $(1)/usr/sbin/trojan +endef + +$(eval $(call BuildPackage,trojan)) diff --git a/trojan/patches/001-force-openssl-version.patch b/trojan/patches/001-force-openssl-version.patch new file mode 100644 index 000000000..d0fea8783 --- /dev/null +++ b/trojan/patches/001-force-openssl-version.patch @@ -0,0 +1,11 @@ +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -43,7 +43,7 @@ if(MSVC) + add_definitions(-DBOOST_DATE_TIME_NO_LIB) + endif() + +-find_package(OpenSSL 1.1.0 REQUIRED) ++find_package(OpenSSL 1.1.1 REQUIRED) + include_directories(${OPENSSL_INCLUDE_DIR}) + target_link_libraries(trojan ${OPENSSL_LIBRARIES}) + if(OPENSSL_VERSION VERSION_GREATER_EQUAL 1.1.1) diff --git a/tuic-client/Makefile b/tuic-client/Makefile new file mode 100644 index 000000000..ad6b44564 --- /dev/null +++ b/tuic-client/Makefile @@ -0,0 +1,75 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Copyright (C) 2017-2020 Yousong Zhou +# Copyright (C) 2021 ImmortalWrt.org + +include $(TOPDIR)/rules.mk + +PKG_NAME:=tuic-client +PKG_VERSION:=1.0.0 +PKG_RELEASE:=1 + +PKG_LICENSE_FILES:=LICENSE +PKG_MAINTAINER:=Tianling Shen + +include $(INCLUDE_DIR)/package.mk + +TUIC_TYPE:=tuic-client-$(PKG_VERSION) +TUIC_FOOT:=unknown-linux-musl +ifeq ($(ARCH),aarch64) + TUIC_ARCH:=$(TUIC_TYPE)-aarch64-$(TUIC_FOOT) + PKG_HASH:=c29eaaf3bc05115acc7453ac26bacf9aff65211b1e8ca7f771b818248bec8601 +else ifeq ($(ARCH),arm) +# Referred to golang/golang-values.mk + ARM_CPU_FEATURES:=$(word 2,$(subst +,$(space),$(call qstrip,$(CONFIG_CPU_TYPE)))) + ifeq ($(ARM_CPU_FEATURES),) + TUIC_ARCH:=$(TUIC_TYPE)-armv7-$(TUIC_FOOT)eabi + PKG_HASH:=33a83ab05dc1a598552bf1f27114502b12c94b6e5333c6ac2def3739c00a4daf + else + TUIC_ARCH:=$(TUIC_TYPE)-armv7-$(TUIC_FOOT)eabihf + PKG_HASH:=e2cc1d81ac376ff2a94976e78c861f5cd21ad809ff5b587ae967e2a63e4c35e4 + endif +else ifeq ($(ARCH),i386) + TUIC_ARCH:=$(TUIC_TYPE)-i686-$(TUIC_FOOT) + PKG_HASH:=67d930b4381a8848bf98acbccb538c73b72ca9dde0aed3705f73e62a9922f758 +else ifeq ($(ARCH),x86_64) + TUIC_ARCH:=$(TUIC_TYPE)-x86_64-$(TUIC_FOOT) + PKG_HASH:=180c562691247a7feddf553706bc8fd5b5b9de3027154f94a767ea907a45e2de +# Set the default value to make OpenWrt Package Checker happy +else + PKG_SOURCE:=dummy + PKG_HASH:=dummy +endif + +define Download/tuic-client + URL:=https://github.com/EAimTY/tuic/releases/download/$(TUIC_TYPE)/ + URL_FILE:=$(TUIC_ARCH) + FILE:=$(TUIC_ARCH) + HASH:=$(PKG_HASH) +endef + +define Package/tuic-client + SECTION:=net + CATEGORY:=Network + SUBMENU:=Web Servers/Proxies + TITLE:=Delicately-TUICed 0-RTT proxy protocol + URL:=https://github.com/EAimTY/tuic/ + DEPENDS:=@USE_MUSL @(aarch64||arm||i386||x86_64) @!(TARGET_x86_geode||TARGET_x86_legacy) +endef + +define Build/Prepare + $(call Build/Prepare/Default) +ifneq ($(CONFIG_PACKAGE_tuic-client),) + $(call Download,tuic-client) +endif +endef + +define Build/Compile +endef + +define Package/tuic-client/install + $(INSTALL_DIR) $(1)/usr/bin + $(INSTALL_BIN) $(DL_DIR)/$(TUIC_ARCH) $(1)/usr/bin/tuic-client +endef + +$(eval $(call BuildPackage,tuic-client)) \ No newline at end of file diff --git a/ucl/Makefile b/ucl/Makefile new file mode 100644 index 000000000..97fc23cbb --- /dev/null +++ b/ucl/Makefile @@ -0,0 +1,69 @@ +# +# Copyright (C) 2019-2020 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:=ucl +PKG_VERSION:=1.03 +PKG_RELEASE:=3 + +PKG_MAINTAINER:=Xingwang Liao +PKG_LICENSE:=GPL-2.0-only +PKG_LICENSE_FILES:=COPYING + +PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz +PKG_SOURCE_URL:=http://www.oberhumer.com/opensource/ucl/download +PKG_HASH:=b865299ffd45d73412293369c9754b07637680e5c826915f097577cd27350348 + +PKG_FIXUP:=autoreconf +PKG_BUILD_PARALLEL:=1 +PKG_INSTALL:=1 + +HOST_FIXUP:=autoreconf +HOST_BUILD_PARALLEL:=1 + +include $(INCLUDE_DIR)/host-build.mk +include $(INCLUDE_DIR)/package.mk + +HOST_BUILD_PREFIX:=$(STAGING_DIR_HOST) + +HOST_CFLAGS += -std=gnu89 +TARGET_CFLAGS += -std=gnu89 + +CONFIGURE_ARGS += \ + --enable-static \ + --enable-shared \ + --disable-asm + +define Package/libucl + SECTION:=libs + CATEGORY:=Libraries + TITLE:=Portable lossless data compression library + URL:=http://www.oberhumer.com/opensource/ucl/ +endef + +define Package/libucl/description +UCL is a portable lossless data compression library written in ANSI C. UCL +implements a number of compression algorithms that achieve an excellent +compression ratio while allowing *very* fast decompression. Decompression +requires no additional memory. +endef + +define Build/InstallDev + $(INSTALL_DIR) $(1)/usr/include/ucl + $(CP) $(PKG_INSTALL_DIR)/usr/include/ucl/ucl{,conf}.h $(1)/usr/include/ucl/ + + $(INSTALL_DIR) $(1)/usr/lib + $(CP) $(PKG_INSTALL_DIR)/usr/lib/libucl.{a,so*} $(1)/usr/lib/ +endef + +define Package/libucl/install + $(INSTALL_DIR) $(1)/usr/lib + $(CP) $(PKG_INSTALL_DIR)/usr/lib/libucl.so.* $(1)/usr/lib/ +endef + +$(eval $(call HostBuild)) +$(eval $(call BuildPackage,libucl)) diff --git a/ucl/patches/001-autoconf-compat.patch b/ucl/patches/001-autoconf-compat.patch new file mode 100644 index 000000000..7dda83d8e --- /dev/null +++ b/ucl/patches/001-autoconf-compat.patch @@ -0,0 +1,11 @@ +--- a/configure.ac ++++ b/configure.ac +@@ -48,7 +48,7 @@ AC_CANONICAL_TARGET + AM_MAINTAINER_MODE + + if test -z "$ac_abs_top_srcdir"; then +- _AC_SRCPATHS(.) ++ _AC_SRCDIRS(.) + fi + if test -r .Conf.settings1; then + . ./.Conf.settings1 diff --git a/upx-static/Makefile b/upx-static/Makefile new file mode 100755 index 000000000..80334d156 --- /dev/null +++ b/upx-static/Makefile @@ -0,0 +1,73 @@ +# Copyright (C) 2020-2022 Hyy2001X + +include $(TOPDIR)/rules.mk + +PKG_NAME:=upx-static +PKG_VERSION:=3.96 +PKG_RELEASE:=20220422 + +ifeq ($(ARCH),x86_64) + PKG_ARCH:=amd64 +endif +ifeq ($(ARCH),i386) + PKG_ARCH:=i386 +endif +ifeq ($(ARCH),mipsel) + PKG_ARCH:=mipsel +endif +ifeq ($(ARCH),mips) + PKG_ARCH:=mips +endif +ifeq ($(ARCH),armeb) + PKG_ARCH:=armeb +endif +ifeq ($(ARCH),arm) + PKG_ARCH:=arm +endif +ifeq ($(ARCH),arm64) + PKG_ARCH:=arm64 +endif +ifeq ($(ARCH),powerpc) + PKG_ARCH:=powerpc +endif +ifeq ($(ARCH),powerpc64) + PKG_ARCH:=powerpc64le +endif + +PKG_FILE:=upx-$(PKG_VERSION).tar.xz +PKG_URL:=https://github.com/upx/upx/releases/download/v$(PKG_VERSION)/upx-$(PKG_VERSION)-$(PKG_ARCH)_linux.tar.xz +PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME) +PKG_HASH:=skip + +include $(INCLUDE_DIR)/package.mk + +define Package/$(PKG_NAME) + SUBMENU:=Compression + SECTION:=utils + CATEGORY:=Utilities + TITLE:=UPX - The Ultimate Packer for eXecutables + URL:=https://upx.github.io +endef + +define Package/$(PKG_NAME)/description +UPX - The Ultimate Packer for eXecutables +endef + +define Build/Prepare + if [ ! -f $(DL_DIR)/$(PKG_FILE) ] ; then \ + wget -q $(PKG_URL) -O $(DL_DIR)/$(PKG_FILE); \ + fi + rm -rf $(PKG_BUILD_DIR) + mkdir -p $(PKG_BUILD_DIR) + xz -d -c $(DL_DIR)/$(PKG_FILE) | tar -x -C $(PKG_BUILD_DIR) +endef + +define Build/Compile +endef + +define Package/$(PKG_NAME)/install + $(INSTALL_DIR) $(1)/bin + $(INSTALL_BIN) $(PKG_BUILD_DIR)/upx-$(PKG_VERSION)-$(PKG_ARCH)_linux/upx $(1)/bin/upx +endef + +$(eval $(call BuildPackage,$(PKG_NAME))) diff --git a/upx-static/README.md b/upx-static/README.md new file mode 100755 index 000000000..d4f809953 --- /dev/null +++ b/upx-static/README.md @@ -0,0 +1,20 @@ +## UPX - The Ultimate Packer for eXecutables +------ + +``` +Usage: upx [-123456789dlthVL] [-qvfk] [-o file] file.. + +Commands: + -1 compress faster -9 compress better + -d decompress -l list compressed file + -t test compressed file -V display version number + -h give more help -L display software license +Options: + -q be quiet -v be verbose + -oFILE write output to 'FILE' + -f force compression of suspicious files + -k keep backup files +file.. executables to (de)compress + +UPX comes with ABSOLUTELY NO WARRANTY; for details visit https://upx.github.io +``` \ No newline at end of file diff --git a/upx/Makefile b/upx/Makefile new file mode 100644 index 000000000..95232298b --- /dev/null +++ b/upx/Makefile @@ -0,0 +1,57 @@ +# SPDX-License-Identifier: GPL-3.0-only +# +# Copyright (C) 2022 ImmortalWrt.org + +include $(TOPDIR)/rules.mk + +PKG_NAME:=upx +PKG_VERSION:=5.0.2 +PKG_RELEASE:=1 + +PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION)-src.tar.xz +PKG_SOURCE_URL:=https://github.com/upx/upx/releases/download/v$(PKG_VERSION) +PKG_HASH:=209b219bbcfa58c249ffe6eba3c244e0910fa8be792b5521e4daf938167f05cc + +PKG_LICENSE:=GPL-2.0-or-later +PKG_LICENSE_FILES:=COPYING LICENSE +PKG_MAINTAINER:=Tianling Shen + +HOST_BUILD_DIR:=$(BUILD_DIR_HOST)/$(PKG_NAME)-$(PKG_VERSION)-src +PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION)-src +CMAKE_BINARY_SUBDIR:=openwrt-build + +PKG_BUILD_FLAGS:=no-mips16 + +include $(INCLUDE_DIR)/host-build.mk +include $(INCLUDE_DIR)/package.mk +include $(INCLUDE_DIR)/cmake.mk + +define Package/upx + SECTION:=utils + CATEGORY:=Utilities + SUBMENU:=Compression + TITLE:=The Ultimate Packer for eXecutables + URL:=https://upx.github.io + DEPENDS:=+libstdcpp +endef + +CMAKE_OPTS:= \ + -DUPX_CONFIG_DISABLE_GITREV=ON \ + -DUPX_CONFIG_DISABLE_SELF_PACK_TEST=ON +CMAKE_HOST_OPTIONS+= $(CMAKE_OPTS) +CMAKE_OPTIONS+= $(CMAKE_OPTS) + +define Package/upx/description + UPX is an advanced executable file compressor. UPX will typically + reduce the file size of programs and DLLs by around 50%-70%, thus + reducing disk space, network load times, download times and + other distribution and storage costs. +endef + +define Package/upx/install + $(INSTALL_DIR) $(1)/usr/bin + $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/upx $(1)/usr/bin/ +endef + +$(eval $(call HostBuild)) +$(eval $(call BuildPackage,upx)) diff --git a/v2dat/Makefile b/v2dat/Makefile new file mode 100644 index 000000000..ae79d69db --- /dev/null +++ b/v2dat/Makefile @@ -0,0 +1,46 @@ +# +# Copyright (C) 2015-2016 OpenWrt.org +# +# This is free software, licensed under the GNU General Public License v3. +# + +include $(TOPDIR)/rules.mk + +PKG_NAME:=v2dat +PKG_SOURCE_DATE:=2022-12-15 +PKG_SOURCE_VERSION:=47b8ee51fb528e11e1a83453b7e767a18d20d1f7 +PKG_RELEASE:=3 + +PKG_SOURCE:=$(PKG_NAME)-$(PKG_SOURCE_DATE).tar.gz +PKG_SOURCE_URL:=https://codeload.github.com/urlesistiana/v2dat/tar.gz/$(PKG_SOURCE_VERSION)? +PKG_HASH:=dca45a31006aca3dba5f543f6990ca755ffb2bde8e533cc2bbe6bac9ec12f157 +PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(PKG_SOURCE_VERSION) + +PKG_LICENSE:=GPL-3.0 +PKG_LICENSE_FILE:=LICENSE +PKG_MAINTAINER:=sbwml + +PKG_BUILD_DEPENDS:=golang/host +PKG_BUILD_PARALLEL:=1 +PKG_USE_MIPS16:=0 +PKG_BUILD_FLAGS:=no-mips16 + +GO_PKG:=github.com/urlesistiana/v2dat + +include $(INCLUDE_DIR)/package.mk +include $(TOPDIR)/feeds/packages/lang/golang/golang-package.mk + +define Package/v2dat + SECTION:=utils + CATEGORY:=Utilities + TITLE:=V2ray geo/ip data unpack tools + URL:=https://github.com/urlesistiana/v2dat + DEPENDS:=$(GO_ARCH_DEPENDS) +endef + +define Package/v2dat/install + $(call GoPackage/Package/Install/Bin,$(1)) +endef + +$(eval $(call GoBinPackage,v2dat)) +$(eval $(call BuildPackage,v2dat)) diff --git a/v2dat/patches/100-format-logtime.patch b/v2dat/patches/100-format-logtime.patch new file mode 100644 index 000000000..a3024e6de --- /dev/null +++ b/v2dat/patches/100-format-logtime.patch @@ -0,0 +1,42 @@ +From 7c6a252ab3f7d9aeb743f9fa8d0cc8c7402f984d Mon Sep 17 00:00:00 2001 +From: sbwml +Date: Wed, 20 Sep 2023 20:52:27 +0800 +Subject: [PATCH] format logtime + +--- + mlog/logger.go | 15 +++++++++++++-- + 1 file changed, 13 insertions(+), 2 deletions(-) + +--- a/mlog/logger.go ++++ b/mlog/logger.go +@@ -1,17 +1,28 @@ + package mlog + +-import "go.uber.org/zap" ++import ( ++ "time" ++ ++ "go.uber.org/zap" ++ "go.uber.org/zap/zapcore" ++) + + var logger = mustInitLogger() + + func mustInitLogger() *zap.Logger { +- l, err := zap.NewDevelopment(zap.WithCaller(false)) ++ config := zap.NewDevelopmentConfig() ++ config.EncoderConfig.EncodeTime = customTimeEncoder ++ l, err := config.Build(zap.WithCaller(false)) + if err != nil { + panic("failed to init mlog:" + err.Error()) + } + return l + } + ++func customTimeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) { ++ enc.AppendString(t.Format("2006-01-02 15:04:05")) ++} ++ + func L() *zap.Logger { + return logger + } diff --git a/v2dat/patches/101-v2dat-update-dependencies.patch b/v2dat/patches/101-v2dat-update-dependencies.patch new file mode 100644 index 000000000..f6c940e77 --- /dev/null +++ b/v2dat/patches/101-v2dat-update-dependencies.patch @@ -0,0 +1,96 @@ +From d5f31a032bae130c83bb50b8679776cf1b33f05d Mon Sep 17 00:00:00 2001 +From: sbwml +Date: Mon, 30 Jun 2025 15:08:51 +0800 +Subject: [PATCH] v2dat: update dependencies + +Signed-off-by: sbwml +--- + go.mod | 14 ++++++-------- + go.sum | 41 +++++++++++++---------------------------- + 2 files changed, 19 insertions(+), 36 deletions(-) + +--- a/go.mod ++++ b/go.mod +@@ -1,18 +1,16 @@ + module github.com/urlesistiana/v2dat + +-go 1.19 ++go 1.22 + + require ( +- github.com/spf13/cobra v1.6.1 +- go.uber.org/zap v1.24.0 +- google.golang.org/protobuf v1.28.1 ++ github.com/spf13/cobra v1.9.1 ++ go.uber.org/zap v1.27.0 ++ google.golang.org/protobuf v1.36.6 + ) + + require ( + github.com/google/go-cmp v0.5.9 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect +- github.com/spf13/pflag v1.0.5 // indirect +- github.com/stretchr/testify v1.8.1 // indirect +- go.uber.org/atomic v1.10.0 // indirect +- go.uber.org/multierr v1.9.0 // indirect ++ github.com/spf13/pflag v1.0.6 // indirect ++ go.uber.org/multierr v1.11.0 // indirect + ) +--- a/go.sum ++++ b/go.sum +@@ -1,42 +1,27 @@ +-github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= +-github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +-github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= ++github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= + github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= + github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +-github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +-github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= + github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= + github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +-github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= + github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= + github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +-github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= + github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= + github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= + github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +-github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= +-github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= +-github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +-github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +-github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +-github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +-github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +-github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +-github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= ++github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= ++github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= ++github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= ++github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= + github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= + github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +-go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= +-go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +-go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= +-go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= +-go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= +-go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= +-go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= +-golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +-google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +-google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= +-google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= ++go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= ++go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= ++go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= ++go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= ++go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= ++go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= ++google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= ++google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= + gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +-gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= + gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= + gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/v2ray-core/Makefile b/v2ray-core/Makefile new file mode 100644 index 000000000..01256fcf1 --- /dev/null +++ b/v2ray-core/Makefile @@ -0,0 +1,79 @@ +# SPDX-License-Identifier: GPL-3.0-only +# +# Copyright (C) 2021 ImmortalWrt.org + +include $(TOPDIR)/rules.mk + +PKG_NAME:=v2ray-core +PKG_VERSION:=5.42.0 +PKG_RELEASE:=1 + +PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz +PKG_SOURCE_URL:=https://codeload.github.com/v2fly/v2ray-core/tar.gz/v$(PKG_VERSION)? +PKG_HASH:=24e2182bf77342165511150db014668723ac4a0c9e72269b0590b0be72875b49 + +PKG_LICENSE:=MIT +PKG_LICENSE_FILES:=LICENSE +PKG_MAINTAINER:=Tianling Shen + +PKG_BUILD_DEPENDS:=golang/host +PKG_BUILD_PARALLEL:=1 +PKG_USE_MIPS16:=0 +PKG_BUILD_FLAGS:=no-mips16 + +GO_PKG:=github.com/v2fly/v2ray-core/v5 +GO_PKG_BUILD_PKG:=$(GO_PKG)/main +GO_PKG_LDFLAGS_X:= \ + $(GO_PKG).build=OpenWrt \ + $(GO_PKG).version=$(PKG_VERSION) + +include $(INCLUDE_DIR)/package.mk +include $(TOPDIR)/feeds/packages/lang/golang/golang-package.mk + +define Package/v2ray/template + TITLE:=A platform for building proxies to bypass network restrictions + SECTION:=net + CATEGORY:=Network + URL:=https://www.v2fly.org +endef + +define Package/v2ray-core + $(call Package/v2ray/template) + DEPENDS:=$(GO_ARCH_DEPENDS) +ca-bundle +endef + +define Package/v2ray-extra + $(call Package/v2ray/template) + TITLE+= (extra resources) + DEPENDS:=v2ray-core + PKGARCH:=all +endef + +define Package/v2ray/description + Project V is a set of network tools that help you to build your own computer network. + It secures your network connections and thus protects your privacy. +endef + +define Package/v2ray-core/description + $(call Package/v2ray/description) +endef + +define Package/v2ray-extra/description + $(call Package/v2ray/description) + + This includes extra resources for v2ray-core. +endef + +define Package/v2ray-core/install + $(call GoPackage/Package/Install/Bin,$(PKG_INSTALL_DIR)) + $(INSTALL_DIR) $(1)/usr/bin/ + $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/main $(1)/usr/bin/v2ray +endef + +define Package/v2ray-extra/install + $(INSTALL_DIR) $(1)/usr/share/v2ray/ + $(CP) $(PKG_BUILD_DIR)/release/extra/* $(1)/usr/share/v2ray/ +endef + +$(eval $(call BuildPackage,v2ray-core)) +$(eval $(call BuildPackage,v2ray-extra)) diff --git a/v2ray-geodata/Makefile b/v2ray-geodata/Makefile new file mode 100644 index 000000000..cc1ee1d07 --- /dev/null +++ b/v2ray-geodata/Makefile @@ -0,0 +1,110 @@ +# SPDX-License-Identifier: GPL-3.0-only +# +# Copyright (C) 2021-2022 ImmortalWrt.org + +include $(TOPDIR)/rules.mk + +PKG_NAME:=v2ray-geodata +PKG_RELEASE:=1 + +PKG_LICENSE_FILES:=LICENSE +PKG_MAINTAINER:=Tianling Shen + +include $(INCLUDE_DIR)/package.mk + +GEOIP_VER:=202512201334 +GEOIP_FILE:=geoip.dat.$(GEOIP_VER) +define Download/geoip + URL:=https://github.com/v2fly/geoip/releases/download/$(GEOIP_VER)/ + URL_FILE:=geoip.dat + FILE:=$(GEOIP_FILE) + HASH:=6878dbacfb1fcb1ee022f63ed6934bcefc95a3c4ba10c88f1131fb88dbf7c337 +endef + +GEOSITE_VER:=20251223103233 +GEOSITE_FILE:=dlc.dat.$(GEOSITE_VER) +define Download/geosite + URL:=https://github.com/v2fly/domain-list-community/releases/download/$(GEOSITE_VER)/ + URL_FILE:=dlc.dat + FILE:=$(GEOSITE_FILE) + HASH:=496c31e5174d75f443efc3980e24d3b3b5b49a70ce348f52b1c2ef181e8da754 +endef + +GEOSITE_IRAN_VER:=202512220045 +GEOSITE_IRAN_FILE:=iran.dat.$(GEOSITE_IRAN_VER) +define Download/geosite-ir + URL:=https://github.com/bootmortis/iran-hosted-domains/releases/download/$(GEOSITE_IRAN_VER)/ + URL_FILE:=iran.dat + FILE:=$(GEOSITE_IRAN_FILE) + HASH:=b10fd8c8f0e74da1e415c020747dabc1881b0b82cdac4a30776b68dbe2c573f1 +endef + +define Package/v2ray-geodata/template + SECTION:=net + CATEGORY:=Network + SUBMENU:=IP Addresses and Names + URL:=https://www.v2fly.org + PKGARCH:=all +endef + +define Package/v2ray-geoip + $(call Package/v2ray-geodata/template) + TITLE:=GeoIP List for V2Ray + PROVIDES:=v2ray-geodata xray-geodata xray-geoip + VERSION:=$(GEOIP_VER)-r$(PKG_RELEASE) + LICENSE:=CC-BY-SA-4.0 +endef + +define Package/v2ray-geosite + $(call Package/v2ray-geodata/template) + TITLE:=Geosite List for V2Ray + PROVIDES:=v2ray-geodata xray-geodata xray-geosite + VERSION:=$(GEOSITE_VER)-r$(PKG_RELEASE) + LICENSE:=MIT +endef + +define Package/v2ray-geosite-ir + $(call Package/v2ray-geodata/template) + TITLE:=Iran Geosite List for V2Ray + PROVIDES:=xray-geosite-ir + VERSION:=$(GEOSITE_IRAN_VER)-r$(PKG_RELEASE) + LICENSE:=MIT +endef + +define Build/Prepare + $(call Build/Prepare/Default) +ifneq ($(CONFIG_PACKAGE_v2ray-geoip),) + $(call Download,geoip) +endif +ifneq ($(CONFIG_PACKAGE_v2ray-geosite),) + $(call Download,geosite) +endif +ifneq ($(CONFIG_PACKAGE_v2ray-geosite-ir),) + $(call Download,geosite-ir) +endif +endef + +define Build/Compile +endef + +define Package/v2ray-geoip/install + $(INSTALL_DIR) $(1)/usr/share/v2ray $(1)/usr/share/xray + $(INSTALL_DATA) $(DL_DIR)/$(GEOIP_FILE) $(1)/usr/share/v2ray/geoip.dat + $(LN) ../v2ray/geoip.dat $(1)/usr/share/xray/geoip.dat +endef + +define Package/v2ray-geosite/install + $(INSTALL_DIR) $(1)/usr/share/v2ray $(1)/usr/share/xray + $(INSTALL_DATA) $(DL_DIR)/$(GEOSITE_FILE) $(1)/usr/share/v2ray/geosite.dat + $(LN) ../v2ray/geosite.dat $(1)/usr/share/xray/geosite.dat +endef + +define Package/v2ray-geosite-ir/install + $(INSTALL_DIR) $(1)/usr/share/v2ray $(1)/usr/share/xray + $(INSTALL_DATA) $(DL_DIR)/$(GEOSITE_IRAN_FILE) $(1)/usr/share/v2ray/iran.dat + $(LN) ../v2ray/iran.dat $(1)/usr/share/xray/iran.dat +endef + +$(eval $(call BuildPackage,v2ray-geoip)) +$(eval $(call BuildPackage,v2ray-geosite)) +$(eval $(call BuildPackage,v2ray-geosite-ir)) diff --git a/v2ray-plugin/Makefile b/v2ray-plugin/Makefile new file mode 100644 index 000000000..55ae19c90 --- /dev/null +++ b/v2ray-plugin/Makefile @@ -0,0 +1,41 @@ +# SPDX-License-Identifier: GPL-3.0-only +# +# Copyright (C) 2020 SharerMax +# Copyright (C) 2021 ImmortalWrt.org + +include $(TOPDIR)/rules.mk + +PKG_NAME:=v2ray-plugin +PKG_VERSION:=5.41.0 +PKG_RELEASE:=1 + +PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz +PKG_SOURCE_URL:=https://codeload.github.com/teddysun/v2ray-plugin/tar.gz/v$(PKG_VERSION)? +PKG_HASH:=75d83f24e60fb7e71e7774732f6ebcfbc00a1b3ae27f4702f75afb14055ce606 + +PKG_LICENSE:=MIT +PKG_LICENSE_FILES:=LICENSE +PKG_MAINTAINER:=madeye + +PKG_BUILD_DEPENDS:=golang/host +PKG_BUILD_PARALLEL:=1 +PKG_USE_MIPS16:=0 +PKG_BUILD_FLAGS:=no-mips16 + +GO_PKG:=github.com/teddysun/v2ray-plugin +GO_PKG_LDFLAGS_X:=main.VERSION=v$(PKG_VERSION) + +include $(INCLUDE_DIR)/package.mk +include $(TOPDIR)/feeds/packages/lang/golang/golang-package.mk + +define Package/v2ray-plugin + SECTION:=net + CATEGORY:=Network + SUBMENU:=Web Servers/Proxies + TITLE:=SIP003 plugin for shadowsocks, based on v2ray + URL:=https://github.com/teddysun/v2ray-plugin + DEPENDS:=$(GO_ARCH_DEPENDS) +ca-bundle +endef + +$(eval $(call GoBinPackage,v2ray-plugin)) +$(eval $(call BuildPackage,v2ray-plugin)) diff --git a/v2raya/Makefile b/v2raya/Makefile new file mode 100644 index 000000000..f553e9132 --- /dev/null +++ b/v2raya/Makefile @@ -0,0 +1,88 @@ +# SPDX-License-Identifier: GPL-3.0-only +# +# Copyright (C) 2021 ImmortalWrt.org + +include $(TOPDIR)/rules.mk + +PKG_NAME:=v2rayA +PKG_VERSION:=2.2.7.4 +PKG_RELEASE:=1 + +PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz +PKG_SOURCE_URL:=https://codeload.github.com/v2rayA/v2rayA/tar.gz/v$(PKG_VERSION)? +PKG_HASH:=801a5488493cef8c2603d596e5982d226c07ad8f4a2969d942922be79169ec29 +PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION)/service + +PKG_LICENSE:=AGPL-3.0-only +PKG_LICENSE_FILES:=LICENSE +PKG_MAINTAINER:=Tianling Shen + +PKG_BUILD_DEPENDS:=golang/host +PKG_BUILD_PARALLEL:=1 +PKG_USE_MIPS16:=0 +PKG_BUILD_FLAGS:=no-mips16 + +GO_PKG:=github.com/v2rayA/v2rayA +GO_PKG_LDFLAGS_X:= \ + $(GO_PKG)/conf.Version=$(PKG_VERSION) \ + $(GO_PKG)/core/iptables.TproxyNotSkipBr=true + +include $(INCLUDE_DIR)/package.mk +include $(TOPDIR)/feeds/packages/lang/golang/golang-package.mk + +TAR_OPTIONS+= --strip-components 1 +TAR_CMD=$(HOST_TAR) -C $(1)/.. $(TAR_OPTIONS) + +define Package/v2raya + TITLE:=A Linux web GUI client of Project V + SECTION:=net + CATEGORY:=Network + SUBMENU:=Web Servers/Proxies + DEPENDS:=$(GO_ARCH_DEPENDS) \ + +ca-bundle \ + +kmod-nft-tproxy \ + +xray-core + URL:=https://v2raya.org +endef + +define Package/v2raya/description + v2rayA is a V2Ray Linux client supporting global transparent proxy, + compatible with SS, SSR, Trojan(trojan-go), PingTunnel protocols. +endef + +define Package/v2raya/conffiles +/etc/v2raya/ +/etc/config/v2raya +endef + +WEB_FILE:=$(PKG_NAME)-web-$(PKG_VERSION).tar.gz +define Download/v2raya-web + URL:=https://github.com/v2rayA/v2rayA/releases/download/v$(PKG_VERSION)/ + URL_FILE:=web.tar.gz + FILE:=$(WEB_FILE) + HASH:=985b083a75b7b34ea9c5151cbc096f6f44fa369a807fea85b435fed5229dae65 +endef + +define Build/Prepare + $(call Build/Prepare/Default) + + ( \ + mkdir -p $(PKG_BUILD_DIR)/server/router/web ; \ + gzip -dc $(DL_DIR)/$(WEB_FILE) | $(HOST_TAR) -C $(PKG_BUILD_DIR)/server/router/web $(TAR_OPTIONS) ; \ + ) +endef + +define Package/v2raya/install + $(call GoPackage/Package/Install/Bin,$(PKG_INSTALL_DIR)) + $(INSTALL_DIR) $(1)/usr/bin/ + $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/v2rayA $(1)/usr/bin/v2raya + + $(INSTALL_DIR) $(1)/etc/config/ + $(INSTALL_CONF) $(CURDIR)/files/v2raya.config $(1)/etc/config/v2raya + $(INSTALL_DIR) $(1)/etc/init.d/ + $(INSTALL_BIN) $(CURDIR)/files/v2raya.init $(1)/etc/init.d/v2raya +endef + +$(eval $(call Download,v2raya-web)) +$(eval $(call GoBinPackage,v2raya)) +$(eval $(call BuildPackage,v2raya)) diff --git a/v2raya/files/v2raya.config b/v2raya/files/v2raya.config new file mode 100644 index 000000000..66b66b243 --- /dev/null +++ b/v2raya/files/v2raya.config @@ -0,0 +1,44 @@ + +config v2raya 'config' + option enabled '0' + + # Listening address + option address '0.0.0.0:2017' + + # Make sure your IPv6 network works fine before you turn it on. + # Optional values: auto, on, off. + option ipv6_support 'auto' + + # Experimental feature. Make sure you have installed nftables. + # Optional values: auto, on, off. + option nftables_support 'auto' + + # Optional values: trace, debug, info, warn or error + option log_level 'info' + + # Maximum number of days to keep log files + option log_max_days '3' + + option log_disable_color '1' + + option log_disable_timestamp '0' + + # Executable v2ray binary path. Auto-detect if put it empty + option v2ray_bin '' + + # Additional v2ray config directory, files in it will be combined with config generated by v2rayA + option v2ray_confdir '' + + # The executable file to run in the transparent proxy life-cycle. + # v2rayA will pass in the --transparent-type (tproxy, redirect) + # and --stage (pre-start, post-start, pre-stop, post-stop) arguments. + option transparent_hook '' + + # The executable file to run in the v2ray-core life-cycle. + # v2rayA will pass in the --stage (pre-start, post-start, pre-stop, post-stop) argument. + option core_hook '' + + # The executable file to run in the v2ray-core life-cycle. + # v2rayA will pass in the --stage (pre-start, post-start, pre-stop, post-stop) argument. + option plugin_manager '' + diff --git a/v2raya/files/v2raya.init b/v2raya/files/v2raya.init new file mode 100755 index 000000000..1e55a187e --- /dev/null +++ b/v2raya/files/v2raya.init @@ -0,0 +1,75 @@ +#!/bin/sh /etc/rc.common +# Copyright (C) 2021 Tianling Shen + +USE_PROCD=1 +START=99 + +CONF="v2raya" +PROG="/usr/bin/v2raya" + +is_enabled() { + local enabled + config_get_bool enabled "$1" "$2" "${3:-0}" + if [ "$enabled" -eq "1" ]; then + return 0 + else + return 1 + fi +} + +append_env() { + procd_append_param env "V2RAYA_$(echo "$1" | tr "[a-z]" "[A-Z]")"="$2" +} + +append_env_arg() { + local value + config_get value "$1" "$2" $3 + [ -n "$value" ] && append_env "$2" "$value" +} + +append_env_bool() { + is_enabled "$1" "$2" && append_env "$2" "true" +} + +start_service() { + config_load "$CONF" + + is_enabled "config" "enabled" || return 1 + + procd_open_instance "$CONF" + procd_set_param command "$PROG" + procd_set_param env XDG_DATA_HOME="/usr/share" + + append_env "config" "/etc/v2raya" + append_env "log_file" "/var/log/v2raya/v2raya.log" + + append_env_arg "config" "address" "0.0.0.0:2017" + append_env_arg "config" "ipv6_support" "auto" + append_env_arg "config" "nftables_support" "auto" + append_env_arg "config" "log_level" "info" + append_env_arg "config" "log_max_days" "3" + append_env_arg "config" "v2ray_bin" + append_env_arg "config" "v2ray_confdir" + append_env_arg "config" "transparent_hook" + append_env_arg "config" "core_hook" + append_env_arg "config" "plugin_manager" + append_env_bool "config" "log_disable_color" + append_env_bool "config" "log_disable_timestamp" + + procd_set_param limits core="unlimited" + procd_set_param limits nofile="1000000 1000000" + procd_set_param respawn + procd_set_param stdout 1 + procd_set_param stderr 1 + + procd_close_instance +} + +reload_service() { + stop + start +} + +service_triggers() { + procd_add_reload_trigger "$CONF" +} diff --git a/webd/Makefile b/webd/Makefile new file mode 100755 index 000000000..45bf4ccfc --- /dev/null +++ b/webd/Makefile @@ -0,0 +1,103 @@ +# Copyright (C) 2020-2022 Hyy2001X +# See more information at https://webd.cf/ + +include $(TOPDIR)/rules.mk + +PKG_NAME:=webd +PKG_VERSION:=20240223 +PKG_RELEASE:=1 + +include $(INCLUDE_DIR)/package.mk + +define Package/$(PKG_NAME) + SECTION:=net + CATEGORY:=Network + TITLE:=A Lightweight self-hosted netdisk application + DEPENDS:=@(arm||x86_64||mipsel||mips||aarch64) + URL:=https://webd.cf +endef + +ifeq ($(ARCH),x86_64) + TARGET_ARCH:=x86_64 + TARGET_BOARD:=musl +endif + +ifeq ($(ARCH),mipsel) + TARGET_ARCH:=mipsel + ifeq ($(BOARD),bcm47xx) + TARGET_BOARD:=musl.brcm47xx-mips74k + endif + ifeq ($(BOARD),ramips) + TARGET_BOARD:=musl.ramips-mt7620 + endif +endif + +ifeq ($(ARCH),mips) + TARGET_ARCH:=mips + ifeq ($(BOARD),ath79) + TARGET_BOARD:=musl.ath79 + endif + ifeq ($(BOARD),bcm63xx) + TARGET_BOARD:=uclibc.brcm63xx + endif + ifeq ($(BOARD),ar71xx) + TARGET_BOARD:=uclibc.ar71xx + endif +endif + +ifeq ($(ARCH),arm) + TARGET_ARCH:=arm + ifeq ($(BOARD),ipq806x) + TARGET_BOARD:=muslgnueabi.ipq806x + endif + ifeq ($(BOARD),ipq40xx) + TARGET_BOARD:=muslgnueabi.ipq40xx + endif + ifeq ($(BOARD),bcm53xx) + TARGET_BOARD:=muslgnueabi.bcm53xx + endif + ifeq ($(BOARD),oxnas) + TARGET_BOARD:=muslgnueabi.oxnas + endif + ifeq ($(BOARD),kirkwood) + TARGET_BOARD:=muslgnueabi.kirkwood + endif + ifeq ($(BOARD),mvebu) + TARGET_BOARD:=muslgnueabi.mvebu-cortexa9 + endif +endif + +ifeq ($(ARCH),aarch64) + TARGET_ARCH:=aarch64 + ifeq ($(BOARD),cortex-a53) + TARGET_BOARD:=musl.brcm2708-bcm2710 + endif + ifeq ($(BOARD),rockchip) + TARGET_BOARD:=musl.rockchip-armv8 + endif +endif + +PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION)-$(TARGET_ARCH)-openwrt-linux-$(TARGET_BOARD).tar.gz +PKG_SOURCE_URL:=https://webd.cf/webd/$(PKG_VERSION)/ +PKG_HASH:=skip + +UNTAR_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION) + +define Build/Prepare + mkdir -p $(UNTAR_DIR) + tar -zxvf $(DL_DIR)/$(PKG_SOURCE) -C $(UNTAR_DIR) +endef + +define Build/Compile +endef + +define Package/$(PKG_NAME)/install + $(INSTALL_DIR) $(1)/usr/bin + $(INSTALL_BIN) $(UNTAR_DIR)/webd/webd $(1)/usr/bin + $(INSTALL_DIR) $(1)/usr/share/$(PKG_NAME) + $(INSTALL_BIN) $(UNTAR_DIR)/webd/web/.player.htm $(1)/usr/share/$(PKG_NAME) + $(INSTALL_DIR) $(1)/etc + $(INSTALL_BIN) $(UNTAR_DIR)/webd/webd.conf $(1)/etc/webd.conf.example +endef + +$(eval $(call BuildPackage,$(PKG_NAME))) diff --git a/xray-core/Makefile b/xray-core/Makefile new file mode 100644 index 000000000..e89af3c4e --- /dev/null +++ b/xray-core/Makefile @@ -0,0 +1,50 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=xray-core +PKG_VERSION:=25.12.8 +PKG_RELEASE:=1 + +PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz +PKG_SOURCE_URL:=https://codeload.github.com/XTLS/Xray-core/tar.gz/v$(PKG_VERSION)? +PKG_HASH:=d4519b2d9bb1871f4d7612aa7a8db1c451573b5a44ac824219bb44d63f404e61 + +PKG_MAINTAINER:=Tianling Shen +PKG_LICENSE:=MPL-2.0 +PKG_LICENSE_FILES:=LICENSE + +PKG_BUILD_DIR:=$(BUILD_DIR)/Xray-core-$(PKG_VERSION) +PKG_BUILD_DEPENDS:=golang/host +PKG_BUILD_PARALLEL:=1 +PKG_USE_MIPS16:=0 +PKG_BUILD_FLAGS:=no-mips16 + +GO_PKG:=github.com/xtls/xray-core +GO_PKG_LDFLAGS:=-s -w +GO_PKG_BUILD_PKG:=$(GO_PKG)/main +GO_PKG_LDFLAGS_X:= \ + $(GO_PKG)/core.build=OpenWrt \ + $(GO_PKG)/core.version=$(PKG_VERSION) + +include $(INCLUDE_DIR)/package.mk +include $(TOPDIR)/feeds/packages/lang/golang/golang-package.mk + +define Package/xray-core + TITLE:=A platform for building proxies to bypass network restrictions + SECTION:=net + CATEGORY:=Network + URL:=https://xtls.github.io + DEPENDS:=$(GO_ARCH_DEPENDS) +ca-bundle +endef + +define Package/xray-core/description + Xray, Penetrates Everything. It helps you to build your own computer network. + It secures your network connections and thus protects your privacy. +endef + +define Package/xray-core/install + $(call GoPackage/Package/Install/Bin,$(PKG_INSTALL_DIR)) + $(INSTALL_DIR) $(1)/usr/bin/ + $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/main $(1)/usr/bin/xray +endef + +$(eval $(call BuildPackage,xray-core)) diff --git a/xray-plugin/Makefile b/xray-plugin/Makefile new file mode 100644 index 000000000..ee2eb9d20 --- /dev/null +++ b/xray-plugin/Makefile @@ -0,0 +1,39 @@ +# SPDX-License-Identifier: GPL-3.0-only +# +# Copyright (C) 2022 ImmortalWrt.org + +include $(TOPDIR)/rules.mk + +PKG_NAME:=xray-plugin +PKG_VERSION:=1.8.24 +PKG_RELEASE:=1 + +PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz +PKG_SOURCE_URL:=https://codeload.github.com/teddysun/xray-plugin/tar.gz/v$(PKG_VERSION)? +PKG_HASH:=dc7d7bff3dfa66c002bd043c7ee60e61aecec2f548275da809f1390c45611836 + +PKG_LICENSE:=MIT +PKG_LICENSE_FILES:=LICENSE +PKG_MAINTAINER:=Tianling Shen + +PKG_BUILD_DEPENDS:=golang/host +PKG_BUILD_PARALLEL:=1 +PKG_USE_MIPS16:=0 +PKG_BUILD_FLAGS:=no-mips16 + +GO_PKG:=github.com/teddysun/xray-plugin + +include $(INCLUDE_DIR)/package.mk +include $(TOPDIR)/feeds/packages/lang/golang/golang-package.mk + +define Package/xray-plugin + SECTION:=net + CATEGORY:=Network + SUBMENU:=Web Servers/Proxies + TITLE:=SIP003 plugin for Shadowsocks, based on Xray + URL:=https://github.com/teddysun/xray-plugin + DEPENDS:=$(GO_ARCH_DEPENDS) +ca-bundle +endef + +$(eval $(call GoBinPackage,xray-plugin)) +$(eval $(call BuildPackage,xray-plugin))