### Author: CuteBi ###
#### Version: 1.1.1 ####
#####    tun    #####

#联网检查
networkCheck() {
	[ ! -f MLBox ] && return
	checkIP() {
		checkVal="$1"
		checkName="$2"
		echo "$checkVal" | grep -qE '[1-9][0-9]{0,2}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' && \
			echo -e "\r✔  $checkName联网成功          " || \
			echo -e "\r✘  $checkName联网失败          "
	}
	echo -n '正在测试DNS...'
	for ntpIP in `./MLBox -timeout=5 -dns="-qtype=A -domain=ntp.aliyun.com" | grep -v 'timeout'`; do break;done
	checkIP "$ntpIP" 'DNS'
	echo -n '正在测试HTTP...'
	httpsIP=`./MLBox -timeout=7 -http='http://182.254.116.116/d?dn=myip.ipip.net&clientip=1' 2>&1 | grep -Ev 'timeout|httpGetResponse'`
	checkIP "$httpsIP" 'HTTP'
	echo -n '正在测试HTTPS...'
	myIP=`./MLBox -timeout=7 -http="-url=https://myip.ipip.net/ -address=${httpsIP%%\;*}:443" 2>&1 | grep -Ev 'timeout|httpGetResponse'`
	checkIP "$myIP" 'HTTPS'
	echo -n '正在测试UDP...'
	currentTime=`./MLBox -timeout=5 -ntp="$ntpIP" | grep -v 'timeout'`
	echo "$currentTime" | grep -qi 'LI:' && \
		echo -e "\r✔  UDP联网成功          " || \
		echo -e "\r✘  UDP联网失败          "

	echo "✄┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄"
	echo "$myIP" | grep 'IP：'
	if [ "$?" != '0' ]; then
		echo "当前 IP： ${httpsIP#*|}" | grep -E "当前 IP： [1-9]\."
	fi
}

#检查脚本更新
checkUpdate() {
	[ ! -f MLBox ] && return
	echo -n '正在检测脚本更新...'
	version='1.1'
	changes=`./MLBox -timeout=7 -http="-address=119.29.186.53:80 -url=http://pros.cutebi.taobao69.cn/clncScript/Changes.php?version=$version" 2>/dev/null | grep -Ev 'timeout|httpGetResponse'`
	if [ -n "$changes" ]; then
		if echo "$changes" | grep -q 'no update'; then
			echo -e "\r✔  当前脚本为最新版          "
		elif echo "$changes" | grep -q "^$version"; then
			echo -e "\r●  当前脚本非最新版          "
			cat <<-EOF
			$changes
			EOF
		fi
	else
		echo -e "\r✘  检测脚本更新失败          "
	fi
}

#自动更新核心
autoUpdataCores() {
	for coreInfo in 'clnc_clnc' 'MLBox_MLBox'; do
		coreName="${coreInfo%%_*}"
		helpFlag="${coreInfo#*_}"
		if [ -f "$coreName" ] && ! ./$coreName -h 2>/dev/null | grep -q "$helpFlag"; then
			./下载核心.sh "$mlHost" "$coreName" "$helpFlag"
		fi
	done
}

#通过包名得到uid
getPackageUid() {
	packageName=${1%%_*}  #过滤包名后面的端口
	if echo $packageName | grep -q '[A-Za-z]'; then
		packageInfo=`grep -oE "^$packageName ([0-9])+" /data/system/packages.list`
		[ $? != 0 ] && return 1
		echo "$1" | grep -qE '_([0-9])+' && \
			echo "${packageInfo#* }_${1#*_}" || \
			echo "${packageInfo#* }"
	else
		echo "$1"
	fi
}

#同时执行iptables和ip6tables
ipts() {
	iptables $@
	ip6tables $@
}

#应用端口操作
appsPortsAction() {
	[ -n "$1" ] && appProtocol="-p $1" || appProtocol=''
	apps="$2"
	jumpAction="$3"
	for app in $apps; do
		uid=`getPackageUid $app` || continue
		uid="${uid%_*}"
		ports=`echo $uid|grep -q '_' && echo "${uid#*_}"`
		ports="${ports//,/ }"
		if [ -z "$ports" ]; then
			ipts -t mangle -I TUN_OUTPUT $appProtocol -m owner --uid $uid -j $jumpAction
			[ "$app" != "$uid" ] && \
				grep -q "uid n=\"999${uid%_*}\"" '/data/system/appops.xml' && \
				ipts -t mangle -I TUN_OUTPUT $appProtocol -m owner --uid $proxyMark${uid} -j $jumpAction
		else
			for p in $appPorts; do
				ipts -t mangle -I TUN_OUTPUT $appProtocol --dport $p -m owner --uid $appUid -j $jumpAction
				[ "$app" != "$uid" ] && \
					grep -q "uid n=\"999${uid%_*}\"" '/data/system/appops.xml' && \
					ipts -t mangle -I TUN_OUTPUT $appProtocol -m owner --uid $proxyMark${uid} -j $jumpAction
			done
		fi
	done
}

#放行
allowService() {
	#IP放行
	for ip in $allowIPs; do
		ipts -t mangle -I TUN_OUTPUT -d $ip -j ACCEPT
		ipts -t mangle -I TUN_PREROUTING -d $ip -j ACCEPT
	done 2>/dev/null  #ipv6跟ipv4不兼容会提示错误
	#本地UDP放行
	appsPortsAction udp "$localUdpAllowApps" ACCEPT
	#本地TCP放行
	appsPortsAction tcp "$localTcpAllowApps" ACCEPT
	#本地全局放行
	appsPortsAction '' "$localAllowApps" ACCEPT
	#共享全局放行
	if [ "$shareAllow" = '1' ]; then
		ipts -t mangle -F TUN_PREROUTING
	else
		#共享UDP放行
		for p in $shareAllowUdpPorts; do
			ipts -t mangle -I TUN_PREROUTING -p udp --dport $p -j ACCEPT
		done
		#共享TCP放行
		for p in $shareAllowTcpPorts; do
			ipts -t mangle -I TUN_PREROUTING -p tcp --dport $p -j ACCEPT
		done
	fi
}

#配置主要ip[6]tables
set_iptables() {
	ipts -t mangle -N TUN_OUTPUT 2>/dev/null
	ipts -t mangle -N TUN_PREROUTING 2>/dev/null
	ipts -t mangle -A TUN_OUTPUT -p udp --dport 67:68 -j ACCEPT #放行dhcp
	#WiFi代理规则
	[ "$allowWifi" = '1' ] && \
		ipts -t mangle -A TUN_OUTPUT -o wlan+ -j ACCEPT || \
		#标记WiFi(WiFi的内网可能是192.168/16和fe80::/10)的DNS
		ipts -t mangle -A TUN_OUTPUT -o wlan+ -p 17 --dport 53 -j  MARK --set-xmark $proxyMark
	##开启只代理部分应用
	if [ -n "$onlyProxyApps" ]; then
		appsPortsAction '' "$onlyProxyApps" "MARK --set-xmark $proxyMark"
	else
		##本机规则
		ipts -t mangle -A TUN_OUTPUT -o tun+ -j ACCEPT
		if [ "$kernelOpt" = 'deny' ]; then
			##禁网
			ipts -t mangle -A TUN_OUTPUT -m owner ! --uid 0-999999999 -j MARK --set-xmark $denyMark || \
				ipts -t mangle -A TUN_OUTPUT -m owner ! --uid 0-99999 -j MARK --set-xmark $denyMark  #部分设备uid最大值为99999
		elif [ "$kernelOpt" = 'allow' ]; then
			##放行
			ipts -t mangle -A TUN_OUTPUT -m owner ! --uid 0-999999999 -j ACCEPT || \
				ipts -t mangle -A TUN_OUTPUT -m owner ! --uid 0-99999 -j ACCEPT  #部分设备uid最大值为99999
		fi 2>/dev/null
		ipts -t mangle -A TUN_OUTPUT -j MARK --set-xmark $proxyMark
		##共享规则
		iptables -t mangle -A TUN_PREROUTING -d 192.168/16 ! -p udp -j ACCEPT
		iptables -t mangle -A TUN_PREROUTING -d 192.168/16 -p udp ! --dport 53 -j ACCEPT
		ipts -t mangle -A TUN_PREROUTING -j MARK --set-xmark $proxyMark
		ipts -t mangle -I PREROUTING ! -i tun+ -g TUN_PREROUTING
		#共享的数据包在FORWARD放行
		ipts -I FORWARD -i tun+ -j ACCEPT
		ipts -I FORWARD -o tun+ -j ACCEPT
	fi
	if [ "$onlyProxyShare" != '1' ]; then
		iptables -t mangle -I OUTPUT ! -o lo ! -d 192.168/16 -m owner ! --gid 3004 -g TUN_OUTPUT
		ip6tables -t mangle -I OUTPUT ! -o lo ! -d fe80::/10 -m owner ! --gid 3004 -g TUN_OUTPUT
	fi
	#不代理IPv6则清空ip6tables规则
	if [ "$ipv6Opt" != 'proxy' ]; then
		ip6tables -t mangle -D PREROUTING ! -i tun+ -g TUN_PREROUTING
		ip6tables -t mangle -D OUTPUT ! -o lo ! -d fe80::/10 -m owner ! --gid 3004 -g TUN_OUTPUT
	fi 2>/dev/null
}

#ip路由
ip_route() {
	ip rule add fwmark $denyMark unreachable pref 99  #禁网
	ip rule add fwmark $proxyMark lookup 101 pref 99  #代理
	ip route add default dev tunDev table 101
	if [ "${ipv6Opt:0:4}" = 'deny' ]; then  #禁网ipv6
		ip -6 rule add unreachable pref 99
	elif [ "$ipv6Opt" = 'proxy' ]; then  #代理ipv6
		ip -6 rule add fwmark $denyMark unreachable pref 99  #禁网
		ip -6 rule add fwmark $proxyMark lookup 101 pref 99  #代理
		ip -6 route add default dev tunDev table 101
	fi
}

#创建tun文件
createTun() {
	echo 1 > /proc/sys/net/ipv4/ip_forward
	[ ! -e "/dev/net/tun" ] && mkdir -p /dev/net && ln -s /dev/tun /dev/net/tun
}

#clnc运行状态检测
clncRunStatus() {
	if [ -f "/proc/$(grep -o ^[0-9]* clnc.pid)/cwd/CuteBi" ]; then
		echo '✔  clnc 正在运行'
	else
		echo '✘  clnc 没有运行'
	fi 2>/dev/null
}

#状态检查
statusCheck() {
	##网卡和IP检测
	ip addr | grep -E "^[0-9]+:|inet" | while read line; do
		if echo $line|grep -qE "^[0-9]+:"; then
			devName=`echo $line|grep -iE "^[0-9]+: [^:]+:"`
			devName=${devName#*: }
			devName=${devName%%:*}
			typeset -L17 devName=${devName%@*} 2>/dev/null
		else
			ipAddr=${line#* }
			ipAddr=${ipAddr%% *}
			echo "$devName$ipAddr"
		fi
	done
	echo "✄┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄"
	echo
	clncRunStatus
	ipts -t mangle -N TUN_OUTPUT 2>&-
	ipts -t mangle -N TUN_PREROUTING 2>&-
	echo
	echo "✄┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄"
	echo '❁ ipv4 mangle表 PREROUTING链:'
	iptables -t mangle -S PREROUTING
	echo '✺ ipv4 mangle表 TUN_PREROUTING链:'
	iptables -t mangle -S TUN_PREROUTING
	echo '❁ ipv4 filter表 FORWARD链:'
	iptables -S FORWARD
	echo '✺ ipv4 mangle表 OUTPUT链:'
	iptables -t mangle -S OUTPUT
	echo '❁ ipv4 mangle表 TUN_OUTPUT链:'
	iptables -t mangle -S TUN_OUTPUT
	echo
	echo "✄┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄"
	echo '❁ ipv6 mangle表 PREROUTING链:'
	ip6tables -t mangle -S PREROUTING
	echo '✺ ipv6 mangle表 TUN_PREROUTING链:'
	ip6tables -t mangle -S TUN_PREROUTING
	echo '❁ ipv6 filter表 FORWARD链:'
	ip6tables -S FORWARD
	echo '✺ ipv6 mangle表 OUTPUT链:'
	ip6tables -t mangle -S OUTPUT
	echo '❁ ipv6 mangle表 TUN_OUTPUT链:'
	ip6tables -t mangle -S TUN_OUTPUT
}

#关闭脚本
clearServer() {
	#关闭程序
	pid=`grep -o ^[0-9]* clnc.pid 2>/dev/null`
	[ -f "/proc/$pid/cwd/CuteBi" ] && kill $pid
	#清理ip路由规则
	{
		while ip rule del pref 99; do :;done
		while ip -6 rule del pref 99; do :;done
		while ipts -t mangle -D OUTPUT ! -o lo -m owner ! --gid 3004 -g TUN_OUTPUT; do :;done
		while ipts -t mangle -D PREROUTING ! -i tun+ -g TUN_PREROUTING; do :;done
		while iptables -t mangle -D OUTPUT ! -o lo ! -d 192.168/16 -m owner ! --gid 3004 -g TUN_OUTPUT; do :;done
		while ip6tables -t mangle -D OUTPUT ! -o lo ! -d fe80::/10 -m owner ! --gid 3004 -g TUN_OUTPUT; do :;done
		ipts -t mangle -F TUN_PREROUTING
		ipts -t mangle -F TUN_OUTPUT
		ipts -t mangle -Z TUN_PREROUTING
		ipts -t mangle -Z TUN_OUTPUT
		ipts -t mangle -X TUN_PREROUTING
		ipts -t mangle -X TUN_OUTPUT
		while ipts -D FORWARD -i tun+ -j ACCEPT; do :;done
		while ipts -D FORWARD -o tun+ -j ACCEPT; do :;done
	} 2>/dev/null
}

#启动脚本
startServer() {
	createTun  #创建tun
	clearServer  #清理规则
	autoUpdataCores  #自动更新核心
	set_iptables  #设置ip[6]tables代理规则
	allowService  #执行放行代码
	#启动clnc
	[ -n "$clnc_dns" ] && export CLNC_INIT_CONFIG_DNS="$clnc_dns"
	./clnc -p clnc.pid -g 3004 -rc "../${modeName}.conf"
	ip_route  #ip路由
}

#ip[6]tables命令获取锁失败则重新执行
aliasIptables() {
	iptables_path=`type iptables 2>/dev/null | grep -o '/.*'`
	ip6tables_path=`type ip6tables 2>/dev/null | grep -o '/.*'`
	if iptables --help | grep -q "\-w"; then
		iptables() { "${iptables_path:-/system/bin/iptables}" -w $@; }
		ip6tables() { "${ip6tables_path:-/system/bin/ip6tables}" -w $@; }
	else
		iptables() {
			"${iptables_path:-/system/bin/iptables}" $@
			[ "$?" = '4' ] && iptables $@
		}
		ip6tables() {
			"${ip6tables_path:-/system/bin/ip6tables}" $@
			[ "$?" = '4' ] && ip6tables $@
		}
	fi
}

init() {
	eval "`grep -v '^\;' ../config.ini`"  #ini文件;开头是注释
	proxyMark='999'
	denyMark='9999'
}

#脚本入口
main() {
	cd "${1%/*}" 
	chmod 777 *
	init
	aliasIptables
	case "$2" in
		'stop')
			clearServer
			echo "✄┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄"
			clncRunStatus
			echo "✄┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄"
		;;
		'start')
			echo "✄┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄"
			startServer
			echo "✄┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄"
			clncRunStatus
			echo "✄┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄"
			networkCheck
			echo "✄┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄"
			checkUpdate
			echo "✄┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄ ┄"
		;;
		'status')
			statusCheck
		;;
	esac
}

main "$0" "$1" 2>&1
