#!/bin/sh /etc/rc.common

# Copyright (C) 2018-2025 Ruilin Peng (Nick) <pymumu@gmail.com>.

# smartdns is free software under the GPLv3 (or later).
# Distributed without any warranty; see the license for details.
# Full license: http://www.gnu.org/licenses/

START=19
STOP=82
NAME=smartdns
USE_PROCD=1
SERVICE_USE_PID=1
SERVICE_WRITE_PID=1
SERVICE_DAEMONIZE=1
SERVICE_PID_FILE="/run/smartdns.pid"
if [ ! -d "/run" ]; then
	SERVICE_PID_FILE="/var/run/smartdns.pid"
fi

SMARTDNS_DOWNLOAD_TMP_DIR="/tmp/smartdns-download"
SMARTDNS_DEFAULT_FORWARDING_FILE="/etc/smartdns/domain-forwarding.list"
SMARTDNS_DEFAULT_DOMAIN_BLOCK_FILE="/etc/smartdns/domain-block.list"
SMARTDNS_CONF_DIR="/etc/smartdns"
SMARTDNS_CONF_DOWNLOAD_DIR="$SMARTDNS_CONF_DIR/conf.d"
SMARTDNS_DOWNLOAD_DIR="$SMARTDNS_CONF_DIR/download"
SMARTDNS_DOMAIN_LIST_DOWNLOAD_DIR="$SMARTDNS_CONF_DIR/domain-set"
SMARTDNS_IP_SET_DOWNLOAD_DIR="$SMARTDNS_CONF_DIR/ip-set"
SMARTDNS_VAR_CONF_DIR="/var/etc/smartdns"
SMARTDNS_CONF="$SMARTDNS_VAR_CONF_DIR/smartdns.conf"
ADDRESS_CONF="$SMARTDNS_CONF_DIR/address.conf"
BLACKLIST_IP_CONF="$SMARTDNS_CONF_DIR/blacklist-ip.conf"
CUSTOM_CONF="$SMARTDNS_CONF_DIR/custom.conf"
SMARTDNS_CONF_TMP="${SMARTDNS_CONF}.tmp"
EXTRA_COMMANDS="updatefiles"
EXTRA_HELP="        updatefiles      Update files"
COREDUMP="0"
RESPAWN="1"
DO_RELOAD="0"

set_forward_dnsmasq()
{
	local PORT="$1"
	addr="127.0.0.1#$PORT"
	# space in suffix is important
	OLD_SERVER="$(uci -q get dhcp.@dnsmasq[0].server) "
	if echo "$OLD_SERVER" | grep "^$addr " >/dev/null 2>&1; then
		return
	fi

	uci -q delete dhcp.@dnsmasq[0].server
	uci -q add_list dhcp.@dnsmasq[0].server="$addr"
	uci -q set dhcp.@dnsmasq[0].noresolv=1
	uci -q set dhcp.@dnsmasq[0].rebind_protection=0
	uci -q set dhcp.@dnsmasq[0].domainneeded=0
	uci commit dhcp
	/etc/init.d/dnsmasq reload
}

stop_forward_dnsmasq()
{
	local OLD_PORT="$1"
	local norestart="$2"
	addr="127.0.0.1#$OLD_PORT"
	OLD_SERVER="$(uci -q get dhcp.@dnsmasq[0].server) "
	if ! echo "$OLD_SERVER" | grep "^$addr " >/dev/null 2>&1; then
		return
	fi
	
	uci -q delete dhcp.@dnsmasq[0].server
	uci -q delete dhcp.@dnsmasq[0].noresolv
	uci -q set dhcp.@dnsmasq[0].rebind_protection=1
	uci -q set dhcp.@dnsmasq[0].domainneeded=1
	uci commit dhcp
	[ "$norestart" != "1" ] && /etc/init.d/dnsmasq reload
}

set_main_dns()
{
	local hostip
	hostip="$(uci -q get network.lan.ipaddr | sed 's/\/.*//g')"
	dnsmasq_port="$(uci -q get dhcp.@dnsmasq[0].port)"
	[ -z "$dnsmasq_port" ] && dnsmasq_port="53"
	
	[ -z "$hostip" ] && return
	[ "$dnsmasq_port" = "53" ] && {
		uci -q set dhcp.@dnsmasq[0].port=0
		uci -q add_list dhcp.lan.dhcp_option="6,$hostip"
	}

	# for some third-party firmware
	redir_dns="$(uci -q get dhcp.@dnsmasq[0].dns_redirect)"
	[ "$redir_dns" = "1" ] && {
		uci -q set dhcp.@dnsmasq[0].dns_redirect=0
		uci -q set dhcp.@dnsmasq[0].old_dns_redirect=1
	}

	uci commit dhcp
	/etc/init.d/dnsmasq reload
}

stop_main_dns()
{
	local norestart="$1"
	hostip="$(uci -q get network.lan.ipaddr)"
	dnsmasq_port="$(uci -q get dhcp.@dnsmasq[0].port)"
	redir_dns="$(uci -q get dhcp.@dnsmasq[0].old_dns_redirect)"
	[ "$dnsmasq_port" != "0" ] && return
	[ "$redir_dns" = "1" ] && {
		uci -q set dhcp.@dnsmasq[0].dns_redirect=1
		uci -q delete dhcp.@dnsmasq[0].old_dns_redirect
	}
	uci -q delete dhcp.@dnsmasq[0].port
	uci -q del_list dhcp.lan.dhcp_option="6,$hostip"
	uci commit dhcp
	[ "$norestart" != "1" ] && /etc/init.d/dnsmasq reload
}

clear_iptable()
{
	local OLD_PORT="$1"
	local ipv6_server=$2

	which iptables >/dev/null 2>&1
	[ $? -ne 0 ] && return

	IPS="$(ifconfig | grep "inet addr" | grep -v ":127" | grep "Bcast" | awk '{print $2}' | awk -F : '{print $2}')"
	for IP in $IPS
	do
		iptables -t nat -D PREROUTING -p udp -d "$IP" --dport 53 -j REDIRECT --to-ports "$OLD_PORT" >/dev/null 2>&1
		iptables -t nat -D PREROUTING -p tcp -d "$IP" --dport 53 -j REDIRECT --to-ports "$OLD_PORT" >/dev/null 2>&1
	done

	[ "$ipv6_server" = 0 ] && return

	IPS="$(ifconfig | grep "inet6 addr" | grep -v " fe80::" | grep -v " ::1" | grep "Global" | awk '{print $3}')"
	for IP in $IPS
	do
		ip6tables -t nat -D PREROUTING -p udp -d "$IP" --dport 53 -j REDIRECT --to-ports "$OLD_PORT" >/dev/null 2>&1
		ip6tables -t nat -D PREROUTING -p tcp -d "$IP" --dport 53 -j REDIRECT --to-ports "$OLD_PORT" >/dev/null 2>&1
	done
}

service_triggers() {
	procd_add_reload_trigger firewall
	procd_add_reload_trigger smartdns
}

conf_append()
{
	echo "$1 $2" >> $SMARTDNS_CONF_TMP
}

get_tz()
{
	SET_TZ=""

	[ -e "/etc/localtime" ] && return

	for tzfile in /etc/TZ /var/etc/TZ
	do
		[ -e "$tzfile" ] || continue
		tz="$(cat $tzfile 2>/dev/null)"
	done

	[ -z "$tz" ] && return

	SET_TZ=$tz
}

load_server()
{
	local section="$1"
	local ADDITIONAL_ARGS=""
	local DNS_ADDRESS=""
	local IS_URI="0"

	config_get_bool enabled "$section" "enabled" "1"
	config_get port "$section" "port" ""
	config_get type "$section" "type" "udp"
	config_get ip "$section" "ip" ""
	config_get tls_host_verify "$section" "tls_host_verify" ""
	config_get no_check_certificate "$section" "no_check_certificate" "0"
	config_get host_name "$section" "host_name" ""
	config_get http_host "$section" "http_host" ""
	config_get server_group "$section" "server_group" ""
	config_get_bool exclude_default_group "$section" "exclude_default_group" "0"
	config_get blacklist_ip "$section" "blacklist_ip" "0"
	config_get check_edns "$section" "check_edns" "0"
	config_get spki_pin "$section" "spki_pin" ""
	config_get addition_arg "$section" "addition_arg" ""
	config_get set_mark "$section" "set_mark" ""
	config_get_bool use_proxy "$section" "use_proxy" "0"
	config_get fallback "$section" "fallback" "0"

	[ "$enabled" = "0" ] && return

	if [ -z "$ip" ] || [ -z "$type" ]; then
		return
	fi

	SERVER="server"
	if [ "$type" = "tcp" ]; then
		SERVER="server-tcp"
	elif [ "$type" = "tls" ]; then
		SERVER="server-tls"
	elif [ "$type" = "https" ]; then
		SERVER="server-https"
	elif [ "$type" = "quic" ]; then
		SERVER="server-quic"
	elif [ "$type" = "h3" ]; then
		SERVER="server-h3"
	fi

	if echo "$ip" | grep "://" >/dev/null 2>&1; then
		IS_URI="1"
	elif echo "$ip" | grep ":"; then
		if ! echo "$ip" | grep -q "\\[" >/dev/null 2>&1; then
			ip="[$ip]"
		fi
	fi

	[ -z "$tls_host_verify" ] || ADDITIONAL_ARGS="$ADDITIONAL_ARGS -tls-host-verify $tls_host_verify"
	[ "$no_check_certificate" = "0" ] || ADDITIONAL_ARGS="$ADDITIONAL_ARGS -no-check-certificate"
	[ -z "$host_name" ] || ADDITIONAL_ARGS="$ADDITIONAL_ARGS -host-name $host_name"
	[ -z "$http_host" ] || ADDITIONAL_ARGS="$ADDITIONAL_ARGS -http-host $http_host"
	[ -z "$server_group" ] || ADDITIONAL_ARGS="$ADDITIONAL_ARGS -group $server_group"
	[ "$exclude_default_group" = "0" ] || ADDITIONAL_ARGS="$ADDITIONAL_ARGS -exclude-default-group"
	[ "$blacklist_ip" = "0" ] || ADDITIONAL_ARGS="$ADDITIONAL_ARGS -blacklist-ip"
	[ "$check_edns" = "0" ] || ADDITIONAL_ARGS="$ADDITIONAL_ARGS -check-edns"
	[ -z "$spki_pin" ] || ADDITIONAL_ARGS="$ADDITIONAL_ARGS -spki-pin $spki_pin"
	[ -z "$set_mark" ] || ADDITIONAL_ARGS="$ADDITIONAL_ARGS -set-mark $set_mark"
	[ "$use_proxy" = "0" ] || ADDITIONAL_ARGS="$ADDITIONAL_ARGS -proxy default-proxy"
	[ "$fallback" = "1" ] && addition_arg="$addition_arg -fallback"

	if [ -z "$port" ] || [ "$IS_URI" = "1" ]; then
		DNS_ADDRESS="$ip"
	else
		DNS_ADDRESS="$ip:$port"
	fi

	conf_append "$SERVER" "$DNS_ADDRESS $ADDITIONAL_ARGS $addition_arg"
}

restart_crond()
{
	/etc/init.d/cron restart >/dev/null 2>&1
}

disable_auto_update()
{
	local no_restart="$1"
	grep "/etc/init.d/smartdns updatefiles" /etc/crontabs/root 1>/dev/null 2>&1
	if [ $? -ne 0 ]; then
		return 
	fi

	sed -i '\@/etc/init.d/smartdns updatefiles@d' /etc/crontabs/root

	if [ "$no_restart" = "1" ]; then
		return
	fi

	restart_crond
}

enable_auto_update()
{
	grep "0 $auto_update_day_time * * $auto_update_week_time /etc/init.d/smartdns updatefiles" /etc/crontabs/root 2>/dev/null
	if [ $? -eq 0 ]; then
		return 
	fi

	disable_auto_update 1
	echo "0 $auto_update_day_time * * $auto_update_week_time /etc/init.d/smartdns updatefiles" >> /etc/crontabs/root
	restart_crond
}

load_domain_rules()
{
	local section="$1"
	local domain_set_args=""
	local domain_set_name="domain"
	local block_domain_set_file=""

	config_get server_group "$section" "server_group" ""
	[ ! -z "$server_group" ] && domain_set_args="$domain_set_args -nameserver $server_group"

	config_get speed_check_mode "$section" "speed_check_mode" ""
	[ ! -z "$speed_check_mode" ] && domain_set_args="$domain_set_args -speed-check-mode $speed_check_mode"

	config_get dualstack_ip_selection "$section" "dualstack_ip_selection" ""
	[ "$dualstack_ip_selection" = "no" ] && domain_set_args="$domain_set_args -dualstack-ip-selection no"
	[ "$dualstack_ip_selection" = "yes" ] && domain_set_args="$domain_set_args -dualstack-ip-selection yes"

	config_get_bool force_aaaa_soa "$section" "force_aaaa_soa" "0"
	[ "$force_aaaa_soa" = "1" ] && domain_set_args="$domain_set_args -address #6"

	config_get ipset_name "$section" "ipset_name" ""
	[ ! -z "$ipset_name" ] && domain_set_args="$domain_set_args -ipset $ipset_name"

	config_get nftset_name "$section" "nftset_name" ""
	[ ! -z "$nftset_name" ] && domain_set_args="$domain_set_args -nftset '$nftset_name'"

	config_get addition_flag "$section" "addition_flag" ""
	[ ! -z "$addition_flag" ] && domain_set_args="$domain_set_args $addition_flag"

	config_get forwarding_domain_set_file "$section" "forwarding_domain_set_file" ""
	[ ! -z "$forwarding_domain_set_file" ] && {
		[ ! -e "$forwarding_domain_set_file" ] && touch $forwarding_domain_set_file
		conf_append "domain-set" "-name ${domain_set_name}-forwarding-file -file '$forwarding_domain_set_file'"
		conf_append "domain-rules" "/domain-set:${domain_set_name}-forwarding-file/ $domain_set_args"
	}

	[ ! -z "$domain_set_args" ] && {
		[ ! -e "$SMARTDNS_DEFAULT_FORWARDING_FILE" ] && touch $SMARTDNS_DEFAULT_FORWARDING_FILE
		conf_append "domain-set" "-name ${domain_set_name}-forwarding-list -file $SMARTDNS_DEFAULT_FORWARDING_FILE"
		conf_append "domain-rules" "/domain-set:${domain_set_name}-forwarding-list/ $domain_set_args"
	}

	config_get block_domain_set_file "$section" "block_domain_set_file"
	[ ! -z "$block_domain_set_file" ] && {
		[ ! -e "$block_domain_set_file" ] && touch $block_domain_set_file
		conf_append "domain-set" "-name ${domain_set_name}-block-file -file '$block_domain_set_file'"
		conf_append "domain-rules" "/domain-set:${domain_set_name}-block-file/ -address #"
	}

	[ ! -e "$SMARTDNS_DEFAULT_DOMAIN_BLOCK_FILE" ] && touch $SMARTDNS_DEFAULT_DOMAIN_BLOCK_FILE
	conf_append "domain-set" "-name ${domain_set_name}-block-list -file $SMARTDNS_DEFAULT_DOMAIN_BLOCK_FILE"
	conf_append "domain-rules" "/domain-set:${domain_set_name}-block-list/ -address #"
}

client_rule_addr_append()
{
	conf_append "client-rules" "$1"
}

load_client_rules()
{
	local section="$1"
	local client_set_args=""
	local client_set_name="$section"
	local block_domain_set_file=""

	config_get_bool enabled "$section" "enabled" "0"
	[ "$enabled" != "1" ] && return

	conf_append "group-begin" "client-group-${section}"

	config_list_foreach "$section" "client_addr" client_rule_addr_append

	config_get client_addr_file "$section" "client_addr_file" ""
	[ ! -z "$client_addr_file" ] && {
		[ ! -e "$client_addr_file" ] && touch $client_addr_file
		conf_append "ip-set" "-name client-rule-list-${client_set_name} -file '$client_addr_file'"
		conf_append "client-rules" "ip-set:client-rule-list-${client_set_name}"
	}

	config_get server_group "$section" "server_group" ""
	[ ! -z "$server_group" ] && conf_append "nameserver $server_group"

	config_get speed_check_mode "$section" "speed_check_mode" ""
	[ ! -z "$speed_check_mode" ] && conf_append "speed-check-mode" "$speed_check_mode"

	config_get dualstack_ip_selection "$section" "dualstack_ip_selection" "0"
	[ "$dualstack_ip_selection" = "0" ] && conf_append "dualstack-ip-selection" "no"

	config_get force_aaaa_soa "$section" "force_aaaa_soa" "0"
	[ "$force_aaaa_soa" = "1" ] && qtype_soa_list="$qtype_soa_list 28"

	config_get force_https_soa "$section" "force_https_soa" "1"
	[ "$force_https_soa" = "1" ] && qtype_soa_list="$qtype_soa_list 65"

	config_get ipset_name "$section" "ipset_name" ""
	[ -z "$ipset_name" ] || conf_append "ipset" "$ipset_name"

	config_get nftset_name "$section" "nftset_name" ""
	[ -z "$nftset_name" ] || conf_append "nftset" "$nftset_name"

	config_list_foreach "$section" "conf_files" conf_append_conf_files

	[ ! -z "$qtype_soa_list" ] && {
		conf_append "force-qtype-SOA" "-"
		conf_append "force-qtype-SOA" "$qtype_soa_list"
	}

	config_get block_domain_set_file "$section" "block_domain_set_file" ""
	[ -e "$block_domain_set_file" ] && {
		conf_append "domain-set" "-name client-block-file-${client_set_name} -file '$block_domain_set_file'"
		conf_append "domain-rules" "/domain-set:client-block-file-${client_set_name}/ -address #"
	}

	conf_append "group-end"
}

load_domain_rule_list()
{
	local section="$1"
	local domain_set_args=""
	local domain_set_name="$section"

	config_get_bool enabled "$section" "enabled" "0"
	[ "$enabled" != "1" ] && return

	config_get server_group "$section" "server_group" ""
	[ ! -z "$server_group" ] && domain_set_args="$domain_set_args -nameserver $server_group"

	config_get block_domain_type "$section" "block_domain_type" ""
	[ "$block_domain_type" = "all" ] && domain_set_args="$domain_set_args -address #"
	[ "$block_domain_type" = "ipv4" ] && domain_set_args="$domain_set_args -address #4"
	[ "$block_domain_type" = "ipv6" ] && domain_set_args="$domain_set_args -address #6"

	config_get speed_check_mode "$section" "speed_check_mode" ""
	[ ! -z "$speed_check_mode" ] && domain_set_args="$domain_set_args -speed-check-mode $speed_check_mode"

	config_get dualstack_ip_selection "$section" "dualstack_ip_selection" ""
	[ "$dualstack_ip_selection" = "no" ] && domain_set_args="$domain_set_args -dualstack-ip-selection no"
	[ "$dualstack_ip_selection" = "yes" ] && domain_set_args="$domain_set_args -dualstack-ip-selection yes"

	config_get_bool force_aaaa_soa "$section" "force_aaaa_soa" "0"
	[ "$force_aaaa_soa" = "1" ] && domain_set_args="$domain_set_args -address #6"

	config_get ipset_name "$section" "ipset_name" ""
	[ ! -z "$ipset_name" ] && domain_set_args="$domain_set_args -ipset $ipset_name"

	config_get nftset_name "$section" "nftset_name" ""
	[ ! -z "$nftset_name" ] && domain_set_args="$domain_set_args -nftset '$nftset_name'"

	config_get domain_list_file "$section" "domain_list_file" ""
	[ -z "$domain_list_file" ] && return

	config_get addition_flag "$section" "addition_flag" ""
	[ ! -z "$addition_flag" ] && domain_set_args="$domain_set_args $addition_flag"
	[ -z "$domain_set_args" ] && return

	[ ! -e "$domain_list_file" ] && touch $domain_list_file
	conf_append "domain-set" "-name domain-rule-list-${domain_set_name} -file '$domain_list_file'"
	conf_append "domain-rules" "/domain-set:domain-rule-list-${domain_set_name}/ $domain_set_args"	
}

ip_rule_addr_append()
{
	conf_append "ip-rules" "$1 $IP_set_args"
}

load_IP_rule_list()
{
	local section="$1"
	local IP_set_args=""
	local IP_set_name="$section"

	config_get_bool enabled "$section" "enabled" "0"
	[ "$enabled" != "1" ] && return

	config_get ip_set_file "$section" "ip_set_file" ""

	config_get_bool whitelist_ip "$section" "whitelist_ip" "0"
	[ "$whitelist_ip" = "1" ] && IP_set_args="$IP_set_args -whitelist-ip"

	config_get_bool blacklist_ip "$section" "blacklist_ip" "0"
	[ "$blacklist_ip" = "1" ] && IP_set_args="$IP_set_args -blacklist-ip"

	config_get_bool ignore_ip "$section" "ignore_ip" "0"
	[ "$ignore_ip" = "1" ] && IP_set_args="$IP_set_args -ignore-ip"

	config_get_bool bogus_nxdomain "$section" "bogus_nxdomain" "0"
	[ "$bogus_nxdomain" = "1" ] && IP_set_args="$IP_set_args -bogus-nxdomain"

	config_get ip_alias "$section" "ip_alias" ""
	[ ! -z "$ip_alias" ] && {
		ip_alias="$(echo "$ip_alias" | sed 's/ /,/g')"
		IP_set_args="$IP_set_args -ip-alias $ip_alias"
	}

	config_get addition_flag "$section" "addition_flag" ""
	[ ! -z "$addition_flag" ] && IP_set_args="$IP_set_args $addition_flag"
	[ -z "$IP_set_args" ] && return

	[ ! -z "$ip_set_file" ] && [ -e "$ip_set_file" ] && {
		conf_append "ip-set" "-name ip-rule-list-file-${section} -file '$ip_set_file'"
		conf_append "ip-rules" "ip-set:ip-rule-list-file-${section} $IP_set_args"
	}

	config_list_foreach "$section" "ip_addr" ip_rule_addr_append
}

conf_append_bind()
{
	local ADDR=""
	local bind_type="$1"
	local port="$2"
	local devices="$3"
	local device=""
	local ipv6_server="$4"
	local ARGS="$5"

	if [ "$ipv6_server" = "1" ]; then
		ADDR="[::]"
	else
		ADDR=""
	fi

	devices=$(echo "$devices" | sed 's/,/ /g')
	[ ! -z "$devices" ] && devices="$devices lo"
	[ -z "$devices" ] && devices="-"

	for device in $devices; do
		device="@$device"
		[ "$device" = "@-" ] && device=""
		conf_append "$bind_type" "$ADDR:$port$device $ARGS"
	done
}

load_second_server()
{
	local section="$1"
	local ARGS=""
	local ADDR=""
	local device=""

	config_get_bool seconddns_enabled "$section" "seconddns_enabled" "0"
	[ "$seconddns_enabled" = "0" ] && return

	config_get seconddns_port "$section" "seconddns_port" "6553"

	config_get_bool seconddns_no_speed_check "$section" "seconddns_no_speed_check" "0"
	[ "$seconddns_no_speed_check" = "1" ] && ARGS="$ARGS -no-speed-check"

	config_get seconddns_server_group "$section" "seconddns_server_group" ""
	[ -z "$seconddns_server_group" ] || ARGS="$ARGS -group $seconddns_server_group"

	config_get_bool seconddns_no_rule_addr "$section" "seconddns_no_rule_addr" "0"
	[ "$seconddns_no_rule_addr" = "1" ] && ARGS="$ARGS -no-rule-addr"

	config_get_bool seconddns_no_rule_nameserver "$section" "seconddns_no_rule_nameserver" "0"
	[ "$seconddns_no_rule_nameserver" = "1" ] && ARGS="$ARGS -no-rule-nameserver"

	config_get_bool seconddns_no_rule_ipset "$section" "seconddns_no_rule_ipset" "0"
	[ "$seconddns_no_rule_ipset" = "1" ] && ARGS="$ARGS -no-rule-ipset"

	config_get_bool seconddns_no_rule_soa "$section" "seconddns_no_rule_soa" "0"
	[ "$seconddns_no_rule_soa" = "1" ] && ARGS="$ARGS -no-rule-soa"

	config_get_bool seconddns_no_dualstack_selection "$section" "seconddns_no_dualstack_selection" "0"
	[ "$seconddns_no_dualstack_selection" = "1" ] && ARGS="$ARGS -no-dualstack-selection"

	config_get_bool seconddns_no_cache "$section" "seconddns_no_cache" "0"
	[ "$seconddns_no_cache" = "1" ] && ARGS="$ARGS -no-cache"

	config_get_bool seconddns_force_aaaa_soa "$section" "seconddns_force_aaaa_soa" "0"
	[ "$seconddns_force_aaaa_soa" = "1" ] && ARGS="$ARGS -force-aaaa-soa"

	config_get_bool seconddns_force_https_soa "$section" "seconddns_force_https_soa" "0"
	[ "$seconddns_force_https_soa" = "1" ] && ARGS="$ARGS -force-https-soa"

	config_get_bool seconddns_no_ip_alias "$section" "seconddns_no_ip_alias" "0"
	[ "$seconddns_no_ip_alias" = "1" ] && ARGS="$ARGS -no-ip-alias"

	config_get seconddns_ipset_name "$section" "seconddns_ipset_name" ""
	[ -z "$seconddns_ipset_name" ] || ARGS="$ARGS -ipset $seconddns_ipset_name"

	config_get seconddns_nftset_name "$section" "seconddns_nftset_name" ""
	[ -z "$seconddns_nftset_name" ] || ARGS="$ARGS -nftset $seconddns_nftset_name"

	config_get_bool bind_device "$section" "bind_device" "0"
	config_get bind_device_name "$section" "bind_device_name" "${lan_device}"
	[ ! -z "$bind_device_name" ] && [ "$bind_device" = "1" ] && device="${bind_device_name}"

	config_get_bool "seconddns_tcp_server" "$section" "seconddns_tcp_server" "1"
	config_get ipv6_server "$section" "ipv6_server" "1"

	config_get seconddns_server_flags "$section" "seconddns_server_flags" ""
	[ -z "$seconddns_server_flags" ] || ARGS="$ARGS $seconddns_server_flags"

	conf_append_bind "bind" "$seconddns_port" "$device" "$ipv6_server" "$ARGS"
	[ "$seconddns_tcp_server" = "1" ] && conf_append_bind "bind-tcp" "$seconddns_port" "$device" "$ipv6_server" "$ARGS"
}

conf_append_conf_files()
{
	local conf_file="$1"

	if [ "$1" != "${1#/}" ]; then
		fullpath="$1"
	else 
		fullpath="$SMARTDNS_CONF_DOWNLOAD_DIR/$conf_file"
	fi

	[ -f "$fullpath" ] && {
		conf_append "conf-file" "'$fullpath'"
	}
}

conf_append_hosts_files()
{
	local hosts_file="$1"

	if [ "$1" != "${1#/}" ]; then
		fullpath="$1"
	else 
		fullpath="$SMARTDNS_DOWNLOAD_DIR/$hosts_file"
	fi

	[ -f "$fullpath" ] && {
		conf_append "hosts-file" "'$fullpath'"
	}
}

load_service()
{
	local section="$1"
	args=""
	local device=""
	dnsmasq_lease_file="$(uci -q get dhcp.@dnsmasq[0].leasefile)"
	dnsmasq_port="$(uci -q get dhcp.@dnsmasq[0].port)"
	resolve_file="$(uci -q get dhcp.@dnsmasq[0].resolvfile)"
	lan_device="$(uci -q get network.lan.device)"

	[ -z "$dnsmasq_lease_file" ] && dnsmasq_lease_file="/tmp/dhcp.leases"
	[ -z "$dnsmasq_port" ] && dnsmasq_port="53"
	[ -z "$resolve_file" ] && resolve_file="/tmp/resolv.conf.d/resolv.conf.auto"

	qtype_soa_list=""

	mkdir -p $SMARTDNS_VAR_CONF_DIR
	rm -f $SMARTDNS_CONF_TMP

	config_get_bool enabled "$section" "enabled" '0'

	config_get server_name "$section" "server_name" ""
	[ -z "$server_name" ] || conf_append "server-name" "$server_name"

	config_get coredump "$section" "coredump" "0"
	[ "$coredump" = "1" ] && COREDUMP="1"

	config_get port "$section" "port" "53"
	config_get ipv6_server "$section" "ipv6_server" "1"
	config_get tcp_server "$section" "tcp_server" "1"
	config_get tls_server "$section" "tls_server" "0"
	config_get tls_server_port "$section" "tls_server_port" "853"
	config_get doh_server "$section" "doh_server" "0"
	config_get doh_server_port "$section" "doh_server_port" "843"
	config_get bind_cert "$section" "bind_cert" ""
	config_get bind_cert_key "$section" "bind_cert_key" ""
	config_get bind_cert_key_pass "$section" "bind_cert_key_pass" ""
	config_get server_flags "$section" "server_flags" ""

	config_get auto_update_week_time "$section" "auto_update_week_time" "*"
	config_get auto_update_day_time "$section" "auto_update_day_time" "5"

	config_get speed_check_mode "$section" "speed_check_mode" ""
	[ ! -z "$speed_check_mode" ] && conf_append "speed-check-mode" "$speed_check_mode"

	config_get dualstack_ip_selection "$section" "dualstack_ip_selection" "0"
	[ "$dualstack_ip_selection" = "0" ] && conf_append "dualstack-ip-selection" "no"

	config_get prefetch_domain "$section" "prefetch_domain" "0"
	[ "$prefetch_domain" = "1" ] && conf_append "prefetch-domain" "yes"

	config_get serve_expired "$section" "serve_expired" "0"
	[ "$serve_expired" = "1" ] && conf_append "serve-expired" "yes"

	config_get cache_size "$section" "cache_size" ""
	[ -z "$cache_size" ] || conf_append "cache-size" "$cache_size"

	config_get resolve_local_hostnames "$section" "resolve_local_hostnames" "1"
	[ "$resolve_local_hostnames" = "1" ] && conf_append "dnsmasq-lease-file" "$dnsmasq_lease_file"

	config_get force_aaaa_soa "$section" "force_aaaa_soa" "0"
	[ "$force_aaaa_soa" = "1" ] && qtype_soa_list="$qtype_soa_list 28"

	config_get force_https_soa "$section" "force_https_soa" "1"
	[ "$force_https_soa" = "1" ] && qtype_soa_list="$qtype_soa_list 65"

	config_get auto_set_dnsmasq "$section" "auto_set_dnsmasq" "1"

	config_get ipset_name "$section" "ipset_name" ""
	[ -z "$ipset_name" ] || conf_append "ipset" "$ipset_name"

	config_get nftset_name "$section" "nftset_name" ""
	[ -z "$nftset_name" ] || conf_append "nftset" "$nftset_name"

	config_get ipset_no_speed "$section" "ipset_no_speed" ""
	[ -z "$ipset_no_speed" ] || conf_append "ipset-no-speed" "$ipset_no_speed"

	config_get nftset_no_speed "$section" "nftset_no_speed" ""
	[ -z "$nftset_no_speed" ] || conf_append "nftset-no-speed" "$nftset_no_speed"

	config_get rr_ttl "$section" "rr_ttl" ""
	[ -z "$rr_ttl" ] || conf_append "rr-ttl" "$rr_ttl"

	config_get rr_ttl_min "$section" "rr_ttl_min" ""
	[ -z "$rr_ttl_min" ] || conf_append "rr-ttl-min" "$rr_ttl_min"

	config_get rr_ttl_max "$section" "rr_ttl_max" ""
	[ -z "$rr_ttl_max" ] || conf_append "rr-ttl-max" "$rr_ttl_max"

	config_get rr_ttl_reply_max "$section" "rr_ttl_reply_max" ""
	[ -z "$rr_ttl_reply_max" ] || conf_append "rr-ttl-reply-max" "$rr_ttl_reply_max"

	config_get log_size "$section" "log_size" "64K"
	[ -z "$log_size" ] || conf_append "log-size" "$log_size"

	config_get log_num "$section" "log_num" "1"
	[ -z "$log_num" ] || conf_append "log-num" "$log_num"

	config_get log_level "$section" "log_level" "error"
	[ -z "$log_level" ]|| conf_append "log-level" "$log_level"

	config_get log_file "$section" "log_file" ""
	[ -z "$log_file" ] || conf_append "log-file" "$log_file"

	config_get log_output_mode "$section" "log_output_mode" ""
	[ "$log_output_mode" = "syslog" ] && conf_append "log-syslog" "yes"

	config_get_bool enable_audit_log "$section" "enable_audit_log" "0"
	[ "$enable_audit_log" = "1" ] && conf_append "audit-enable" "yes"

	config_get audit_log_size "$section" "audit_log_size" "64K"
	[ -z "$audit_log_size" ] || conf_append "audit-size" "$audit_log_size"

	config_get audit_log_num "$section" "audit_log_num" "1"
	[ -z "$audit_log_num" ] || conf_append "audit-num" "$audit_log_num"

	config_get audit_log_file "$section" "audit_log_file" ""
	[ -z "$audit_log_file" ] || conf_append "audit-file" "$audit_log_file"

	config_get audit_log_output_mode "$section" "audit_log_output_mode" ""
	[ "$audit_log_output_mode" = "syslog" ] && conf_append "audit-syslog" "yes"

	config_get response_mode "$section" "response_mode" ""
	[ -z "$response_mode" ] || conf_append "response-mode" "$response_mode"

	config_get_bool enable_auto_update "$section" "enable_auto_update" "0"
	[ "$enabled" = "1" -a "$enable_auto_update" = "1" ] && enable_auto_update || disable_auto_update

	config_get_bool bind_device "$section" "bind_device" "0"
	config_get bind_device_name "$section" "bind_device_name" "${lan_device}"
	[ ! -z "$bind_device_name" ] && [ "$bind_device" = "1" ] && device="${bind_device_name}"

	config_get cache_file "$section" "cache_file" "$SMARTDNS_CONF_DIR/smartdns.cache"

	config_get_bool cache_persist "$section" "cache_persist" "0"
	[ "$cache_persist" = "1" ] && {
		conf_append "cache-persist" "yes"
		conf_append "cache-file" "$cache_file"
	}

	[ "$cache_persist" = "0" ] && {
		conf_append "cache-persist" "no"
		[ -f "$cache_file" ] && rm -f "$cache_file"
	}

	config_get proxy_server "$section" "proxy_server" ""
	[ -z "$proxy_server" ] || conf_append "proxy-server" "$proxy_server -name default-proxy"
	
	config_get dns64 "$section" "dns64" ""
	[ -z "$dns64" ] || conf_append "dns64" "$dns64"

	config_get ddns_domain "$section" "ddns_domain" ""
	[ -z "$ddns_domain" ] || conf_append "ddns-domain" "$ddns_domain"

	config_get local_domain "$section" "local_domain" ""
	[ -z "$local_domain" ] || conf_append "local-domain" "$local_domain"

	config_get_bool mdns_lookup "$section" "mdns_lookup" "0"
	[ "$mdns_lookup" = "1" ] && conf_append "mdns-lookup" "yes"

	config_get redirect "$section" "redirect" ""
	config_get old_port "$section" "old_port" "0"
	config_get old_enabled "$section" "old_enabled" "0"
	config_get old_auto_set_dnsmasq "$section" "old_auto_set_dnsmasq" "0"

	[ -z "$qtype_soa_list" ] || conf_append "force-qtype-SOA" "$qtype_soa_list"
	[ -e "$resolve_file" ] && conf_append "resolv-file" "$resolve_file"

	# upgrade old configuration
	if [ "$redirect" = "redirect" ] || [ "$redirect" = "dnsmasq-upstream" ] || [ "$redirect" = "none" ]; then
		[ "$redirect" = "redirect" ] && {
			clear_iptable "$port"
			clear_iptable "$old_port"
			uci -q delete smartdns.@smartdns[0].port
			port="53"
		}

		[ "$redirect" = "dnsmasq-upstream" ] && {
			stop_forward_dnsmasq "$port"
			stop_forward_dnsmasq "$old_port"
			auto_set_dnsmasq="1"
			uci -q set smartdns.@smartdns[0].auto_set_dnsmasq="1"
		}

		[ "$redirect" = "none" ] && {
			auto_set_dnsmasq="0"
			uci -q set smartdns.@smartdns[0].auto_set_dnsmasq="0"
		}
		uci -q delete smartdns.@smartdns[0].redirect
		uci -q delete smartdns.@smartdns[0].old_redirect
	fi

	uci -q delete smartdns.@smartdns[0].old_port
	uci -q delete smartdns.@smartdns[0].old_enabled
	uci -q delete smartdns.@smartdns[0].old_auto_set_dnsmasq
	uci -q set smartdns.@smartdns[0].old_port="$port"
	uci -q set smartdns.@smartdns[0].old_enabled="$enabled"
	uci -q set smartdns.@smartdns[0].old_auto_set_dnsmasq="$auto_set_dnsmasq"
	uci commit smartdns

	# disable service
	[ "$enabled" = "0" ] && {
		[ "$old_enabled" = "0" ] && return 1
		[ "$old_port" = "53" ] && [ "$old_auto_set_dnsmasq" = "1" ] && stop_main_dns "0"
		[ "$old_port" != "53" ] && [ "$old_auto_set_dnsmasq" = "1" ] && stop_forward_dnsmasq "$old_port" "0"
		disable_auto_update
		return 1
	}

	# change port
	[ "$old_port" != "$port" ] && {
		[ "$old_port" = "53" ] && {
			no_restart_dnsmasq="1"
			[ "$auto_set_dnsmasq" = "0" ] && no_restart_dnsmasq="0"
			[ "$old_auto_set_dnsmasq" = "1" ] && stop_main_dns "$no_restart_dnsmasq"
		}
		[ "$old_port" != "53" ] && [ "$old_auto_set_dnsmasq" = "1" ] && stop_forward_dnsmasq "$old_port" "1"
	}

	# start service
	[ "$port" = "53" ] && {
		[ "$auto_set_dnsmasq" = "1" ] && set_main_dns
		[ "$auto_set_dnsmasq" = "0" ] && [ "$old_auto_set_dnsmasq" = "1" ] && stop_main_dns "0"
	}
	[ "$port" != "53" ] && {
		[ "$auto_set_dnsmasq" = "1" ] && set_forward_dnsmasq "$port"
		[ "$auto_set_dnsmasq" = "0" ] && [ "$old_auto_set_dnsmasq" = "1" ] && stop_forward_dnsmasq "$old_port" "0"
	}

	conf_append_bind "bind" "$port" "$device" "$ipv6_server" "$server_flags"
	[ "$tcp_server" = "1" ] && conf_append_bind "bind-tcp" "$port" "$device" "$ipv6_server" "$server_flags"
	[ "$tls_server" = "1" ] && conf_append_bind "bind-tls" "$tls_server_port" "$device" "$ipv6_server" "$server_flags"
	[ "$doh_server" = "1" ] && conf_append_bind "bind-https" "$doh_server_port" "$device" "$ipv6_server" "$server_flags"

	[ ! -z "$bind_cert" ] && conf_append "bind-cert-file" "$bind_cert"
	[ ! -z "$bind_cert_key" ] && conf_append "bind-cert-key-file" "$bind_cert_key"
	[ ! -z "$bind_cert_key_pass" ] && conf_append "bind-cert-key-pass" "$bind_cert_key_pass"

	load_second_server "$section"

	config_foreach load_server "server"

	config_list_foreach "$section" "conf_files" conf_append_conf_files

	config_list_foreach "$section" "hosts_files" conf_append_hosts_files

	config_foreach load_client_rules "client-rule"

	config_foreach load_domain_rules "domain-rule"

	config_foreach load_domain_rule_list "domain-rule-list"

	config_foreach load_IP_rule_list "ip-rule"

	config_foreach load_IP_rule_list "ip-rule-list"

	config_get_bool ui "$section" "ui" '0'

	[ "$ui" = "1" ] && {
		config_get ui_port "$section" "ui_port" "6080"
		config_get ui_data_dir "$section" "ui_data_dir" "/var/lib/smartdns"
		config_get ui_log_max_age "$section" "ui_log_max_age" "30"

		ui_log_max_age_s=$((ui_log_max_age * 86400))

		conf_append "plugin" "smartdns_ui.so"
		conf_append "smartdns-ui.www-root" "/usr/share/smartdns/wwwroot"
		conf_append "smartdns-ui.ip" "http://[::]:$ui_port"
		conf_append "data-dir" "$ui_data_dir"
		conf_append "smartdns-ui.max-query-log-age" "$ui_log_max_age_s"
	}

	{
		echo "conf-file $ADDRESS_CONF"
		echo "conf-file $BLACKLIST_IP_CONF"
		echo "conf-file $CUSTOM_CONF"
	} >> $SMARTDNS_CONF_TMP
	mv $SMARTDNS_CONF_TMP $SMARTDNS_CONF

	procd_open_instance "smartdns"
	[ "$COREDUMP" = "1" ] && {
		args="$args -S"
		procd_set_param limits core="unlimited"
	}

	get_tz
	[ -z "$SET_TZ" ] || procd_set_param env TZ="$SET_TZ"

	procd_set_param command /usr/sbin/smartdns -f -c $SMARTDNS_CONF $args
	[ "$RESPAWN" = "1" ] &&	procd_set_param respawn ${respawn_threshold:-3600} ${respawn_timeout:-5} ${respawn_retry:-5}
	procd_set_param file "$SMARTDNS_CONF"
	procd_set_param term_timeout 60
	procd_close_instance
}

unload_service()
{
	local section="$1"

	[ "$DO_RELOAD" = "1" ] && return 0

	config_get_bool enabled "$section" "enabled" '0'
	dnsmasq_port="$(uci -q get dhcp.@dnsmasq[0].port)"
	config_get port "$section" "port" "53"
	config_get auto_set_dnsmasq "$section" "auto_set_dnsmasq" "0"
	config_get old_enabled "$section" "old_enabled" "0"
	config_get old_port "$section" "old_port" "0"
	config_get old_auto_set_dnsmasq "$section" "old_auto_set_dnsmasq" "0"
	[ -z "${dnsmasq_port}" ] && dnsmasq_port="53"

	[ "$enabled" = "1" ] && {
		[ "$old_enabled" = "0" ] && return 1
		[ "$old_port" = "53" ] && [ "$old_auto_set_dnsmasq" = "1" ] && stop_main_dns "0"
		[ "$old_port" != "53" ] && [ "$old_auto_set_dnsmasq" = "1" ] && stop_forward_dnsmasq "$old_port" "0"
	}
}

download_file() {
	local section="$1"

	config_get url "$section" "url" ""
	config_get name "$section" "name" ""
	config_get filetype "$section" "type" ""
	config_get_bool use_proxy "$section" "use_proxy" "0"

	[ -z "$url" ] && return 0
	[ -z "$name" ] && return 0
	[ -z "$filetype" ] && return 0

	echo "download $filetype file $name from $url"
	[ "$use_proxy" = "1" ] && {
		proxy="$(uci -q get smartdns.@smartdns[0].proxy_server)"
		[ ! -z "$proxy" ] && {
			export http_proxy="$proxy"
			export https_proxy="$proxy"
		}
	}
	wget --timeout 120 -q -O "$SMARTDNS_DOWNLOAD_TMP_DIR/$name" "$url"
	if [ $? -ne 0 ]; then
		echo "download file $name failed"
		return 1
	fi

	echo "download file $name success"
	if [ "$filetype" = "list" ]; then
		mv "$SMARTDNS_DOWNLOAD_TMP_DIR/$name" "$SMARTDNS_DOMAIN_LIST_DOWNLOAD_DIR/$name"	
	elif [ "$filetype" = "config" ]; then
		mv "$SMARTDNS_DOWNLOAD_TMP_DIR/$name" "$SMARTDNS_CONF_DOWNLOAD_DIR/$name"	
	elif [ "$filetype" = "ip-set" ]; then
		mv "$SMARTDNS_DOWNLOAD_TMP_DIR/$name" "$SMARTDNS_IP_SET_DOWNLOAD_DIR/$name"
	else 
		mv "$SMARTDNS_DOWNLOAD_TMP_DIR/$name" "$SMARTDNS_DOWNLOAD_DIR/$name"
	fi
}

check_and_add_entry() {
	local docommit=0
	uci -q get smartdns.@smartdns[0] >/dev/null
	if [ $? -ne 0 ]; then
		uci -q add smartdns smartdns >/dev/null
		docommit=1
	fi

	uci -q get smartdns.@client-rule[0] >/dev/null
	if [ $? -ne 0 ]; then
		uci -q add smartdns client-rule >/dev/null
		docommit=1
	fi
	
	uci -q get smartdns.@domain-rule[0] >/dev/null
	if [ $? -ne 0 ]; then
		uci -q add smartdns domain-rule >/dev/null
		docommit=1
	fi

	uci -q get smartdns.@ip-rule[0] >/dev/null
	if [ $? -ne 0 ]; then
		uci -q add smartdns ip-rule >/dev/null
		docommit=1
	fi

	if [ "$docommit" = "1" ]; then
		uci -q commit smartdns >/dev/null
	fi

	if [ ! -d "$SMARTDNS_DOMAIN_LIST_DOWNLOAD_DIR" ]; then
		mkdir -p "$SMARTDNS_DOMAIN_LIST_DOWNLOAD_DIR"
	fi

	if [ ! -d "$SMARTDNS_CONF_DOWNLOAD_DIR" ]; then
		mkdir -p "$SMARTDNS_CONF_DOWNLOAD_DIR"
	fi
}

updatefiles() {
	config_load "smartdns"
	[ ! -d "$SMARTDNS_DOWNLOAD_TMP_DIR" ] && mkdir -p "$SMARTDNS_DOWNLOAD_TMP_DIR"
	config_foreach download_file "download-file"
	rm -rf "$SMARTDNS_DOWNLOAD_TMP_DIR" >/dev/null 2>&1
	reload_service
}

service_stopped()
{
	config_load "smartdns"
	config_foreach unload_service "smartdns"
}

start_service()
{
	check_and_add_entry
	config_load "smartdns"
	config_foreach load_service "smartdns"
}

reload_service()
{
	DO_RELOAD="1"
	stop
	start
	DO_RELOAD="0"
}
