#!/bin/sh /etc/rc.common
# Copyright (C) 2021-2025 Lienol <lawlienol@gmail.com>

START=99
STOP=10

USE_PROCD=1
PROG=/usr/sbin/openvpn

CONFIG="luci-app-openvpn-server"
OVPN_PATH=/usr/share/openvpn-server
TMP_OVPN_PATH=/var/etc/openvpn-server

port=$(uci -q get ${CONFIG}.server.port || echo "1194")
proto=$(uci -q get ${CONFIG}.server.proto || echo "udp")
ip_segment=$(uci -q get ${CONFIG}.server.ip_segment || echo "172.30.1.0")
subnet_mask=$(uci -q get ${CONFIG}.server.subnet_mask || echo "255.255.255.0")
ipv6=$(uci -q get ${CONFIG}.server.ipv6 || echo "0")
[ "${ipv6}" -eq 1 ] && [ -z "$(command -v ip6tables)" ] && ipv6=0
ip_prefix="$(ipcalc.sh ${ip_segment} ${subnet_mask} | grep "PREFIX" | awk -F '=' '{print $2}')"

ipt_flag="OpenVPN Server"

ipt_rule() {
	if [ "$1" = "add" ]; then
		iptables -w -t nat -I POSTROUTING -s ${ip_segment%.*}.0/${ip_prefix} -m comment --comment "${ipt_flag}" -j MASQUERADE 2>/dev/null
		iptables -w -I FORWARD -s ${ip_segment%.*}.0/${ip_prefix} -m comment --comment "${ipt_flag}" -j ACCEPT 2>/dev/null
		iptables -w -I INPUT -p ${proto} --dport ${port} -m comment --comment "${ipt_flag}" -j ACCEPT 2>/dev/null
		iptables -w -t mangle -I OUTPUT -p ${proto} --sport ${port} -m comment --comment "${ipt_flag}" -j RETURN 2>/dev/null
		
		[ "${ipv6}" -eq 1 ] && {
			ip6tables -w -I INPUT -p ${proto} --dport ${port} -m comment --comment "${ipt_flag}" -j ACCEPT 2>/dev/null
			ip6tables -w -t mangle -I OUTPUT -p ${proto} --sport ${port} -m comment --comment "${ipt_flag}" -j RETURN 2>/dev/null
		}
	else
		ipt_del() {
			for i in $(seq 1 $($1 -nL $2 | grep -c "${ipt_flag}")); do
				local index=$($1 --line-number -nL $2 | grep "${ipt_flag}" | head -1 | awk '{print $1}')
				$1 -w -D $2 $index 2>/dev/null
			done
		}
		ipt_del "iptables" "FORWARD"
		ipt_del "iptables" "INPUT"
		ipt_del "iptables -t nat" "POSTROUTING"
		ipt_del "iptables -t mangle" "OUTPUT"
		ipt_del "ip6tables" "INPUT"
		ipt_del "ip6tables -t mangle" "OUTPUT"
	fi
}

gen_include() {
	echo '#!/bin/sh' > /var/etc/${CONFIG}.include
	extract_rules() {
		local _ipt="iptables"
		[ "$1" == "6" ] && _ipt="ip6tables"
		
		echo "*$2"
		${_ipt}-save -t $2 | grep "${ipt_flag}" | sed -e "s#\-A#\-I#g"
		echo 'COMMIT'
	}
	cat <<-EOF >> /var/etc/${CONFIG}.include
		iptables-save -c | grep -v "${ipt_flag}" | iptables-restore -w -c
		iptables-restore -w -n <<-EOT
		$(extract_rules 4 filter)
		$(extract_rules 4 nat)
		$(extract_rules 4 mangle)
		EOT
		[ "${ipv6}" -eq 1 ] && {
			ip6tables-save -c | grep -v "${ipt_flag}" | ip6tables-restore -w -c
			ip6tables-restore -w -n <<-EOT
			$(extract_rules 6 filter)
			$(extract_rules 6 mangle)
			EOT
		}
		exit 0
	EOF
	return 0
}

start_service() {
	local enabled=$(uci -q get ${CONFIG}.server.enabled)
	[ "${enabled}" -eq 1 ] || return 1
	local lzo=$(uci -q get ${CONFIG}.server.lzo)
	[ "${lzo}" -eq 1 ] && lzo_conf="comp-lzo"
	mkdir -p ${TMP_OVPN_PATH}
	
	_ca=${OVPN_PATH}/ca.crt
	_cert=${OVPN_PATH}/server.crt
	_key=${OVPN_PATH}/server.key
	_dh=${OVPN_PATH}/dh.pem
	
	mkdir -p ${TMP_OVPN_PATH}/ccd
	
	_auth_path=${TMP_OVPN_PATH}/auth
	touch ${_auth_path}
	local _users=$(uci -q show ${CONFIG} | grep "=users" | cut -d '.' -sf2 | cut -d '=' -sf 1)
	[ -n "${_users}" ] && {
		for _user in ${_users}; do
			local u_enabled=$(uci -q get ${CONFIG}.${_user}.enabled)
			[ "${u_enabled}" -eq 1 ] || continue
			
			local u_username=$(uci -q get ${CONFIG}.${_user}.username)
			[ -n "${u_username}" ] || continue
			
			local u_password=$(uci -q get ${CONFIG}.${_user}.password)
			[ -n "${u_password}" ] || continue
			
			echo "${u_username} ${u_password}" >> ${_auth_path}
			
			local u_ipaddress=$(uci -q get ${CONFIG}.${_user}.ipaddress)
			[ -n "${u_ipaddress}" ] && echo "ifconfig-push ${u_ipaddress} ${subnet_mask}" >> ${TMP_OVPN_PATH}/ccd/${u_username}
			
			local u_routes=$(uci -q get ${CONFIG}.${_user}.routes)
			for u_route in ${u_routes}; do
				eval "$(ipcalc.sh ${u_route})"
				echo "iroute ${IP} ${NETMASK}" >> ${TMP_OVPN_PATH}/ccd/${u_username}
			done
			unset u_enabled u_username u_password u_ipaddress u_routes
		done
	}
	
	SCRIPT_PATH=${OVPN_PATH}/script
	chmod 0755 ${SCRIPT_PATH}/*
	
	local extra_config=$(uci -q get ${CONFIG}.server.extra_config)
	
	cat <<-EOF >> ${TMP_OVPN_PATH}/server.conf
		management 127.0.0.1 17777
		port ${port}
		proto ${proto}$([ "${ipv6}" -eq 1 ] && echo "6")
		dev ovpn_server
		dev-type tun
		ca ${_ca}
		cert ${_cert}
		key ${_key}
		dh ${_dh}
		topology subnet
		auth-user-pass-verify ${SCRIPT_PATH}/auth_verify.sh via-env
		verify-client-cert none
		username-as-common-name
		client-config-dir ${TMP_OVPN_PATH}/ccd
		server ${ip_segment} ${subnet_mask}
		ifconfig-pool-persist ipp.txt
		client-to-client
		client-connect ${SCRIPT_PATH}/client_connect.sh
		client-disconnect ${SCRIPT_PATH}/client_disconnect.sh
		up ${SCRIPT_PATH}/up.sh
		down ${SCRIPT_PATH}/down.sh
		keepalive 10 120
		${lzo_conf}
		persist-key
		persist-tun
		script-security 3
		status openvpn-status.log
		log         openvpn.log
		log-append  openvpn.log
		verb 3
		push "topology subnet"
		push "redirect-gateway def1 bypass-dhcp"
		push "dhcp-option DNS ${ip_segment%.*}.1"
		${extra_config}
	EOF
	
	procd_open_instance "$CONFIG"
	procd_set_param command "$PROG"	\
		--cd "${TMP_OVPN_PATH}" \
		--config "server.conf"
	procd_set_param file "${TMP_OVPN_PATH}/server.conf"
	procd_set_param file "${_ca}"
	procd_set_param file "${_cert}"
	procd_set_param file "${_key}"
	procd_set_param file "${_dh}"
	procd_set_param term_timeout 15
	procd_set_param respawn
	procd_append_param respawn 3600
	procd_append_param respawn 5
	procd_append_param respawn -1
	procd_close_instance
	
	ipt_rule add
	gen_include
}

stop_service() {
	ipt_rule del
	rm -rf /var/etc/${CONFIG}.include
	rm -rf ${TMP_OVPN_PATH}
}

reload_service() {
	restart
}

service_triggers() {
	procd_add_reload_trigger "${CONFIG}"
}
