465 lines
18 KiB
Bash
465 lines
18 KiB
Bash
#!/bin/sh /etc/rc.common
|
|
#
|
|
# Copyright (C) 2019 p.t. <peter-tank@github.com>
|
|
#
|
|
# This is free software, licensed under the GNU General Public License v3.
|
|
# See /LICENSE for more information.
|
|
#
|
|
|
|
START=30
|
|
|
|
USE_PROCD=1
|
|
INIT_TRACE=
|
|
EXTRA_COMMANDS=
|
|
|
|
PROG=/usr/sbin/dnscrypt-proxy
|
|
CONFIG_DIR=/var/etc
|
|
USER=nobody
|
|
NAME=dnscrypt-proxy
|
|
|
|
SERVICE_DAEMONIZE=
|
|
SERVICE_USE_PID=1
|
|
SERVICE_MATCH_EXEC=1
|
|
SERVICE_STOP_TIME=15
|
|
SERVICE_PID_FILE=/var/run/$NAME.pid
|
|
|
|
LUCI_STATUS=/var/run/luci-reload-status
|
|
LOG_FILE="/var/etc/dnscrypt"
|
|
CACHE_DIR="/usr/share/dnscrypt-proxy"
|
|
DIRECT_IPSETS="vpsiplist localnetwork ss_spec_wan_ac"
|
|
|
|
# has side effects when there is a option names the same 'addrs' in shadowsocksr config file
|
|
# keep this uci function excuted at the bottom of scripts
|
|
uci_filter_resolver_addrs() {
|
|
local __PKGNAME="${NAME}"
|
|
local __SECTIONTYPE="server_addr"
|
|
local ret
|
|
|
|
filter_resolver_addrs()
|
|
{
|
|
local __TYPE="$1"
|
|
local __OPTION="$2"
|
|
set | sed -ne "s/^CONFIG_.*_addrs='\([^']*\)'$/\1/p" | sed -e "s/[, ]/\n/g" | sort | uniq
|
|
}
|
|
|
|
# config_load "${__PKGNAME}"
|
|
# tricky way first for all server_addr addrs in DNSCrypt ACL
|
|
ret=$(filter_resolver_addrs "${__SECTIONTYPE}" "addrs")
|
|
# or get all addrs from all resolvers configured.
|
|
[ -z "${ret}" ] && {
|
|
CONFIG_APPEND=1
|
|
config_list_foreach "ns1" "resolvers" config_load
|
|
ret=$(filter_resolver_addrs ".*" "addrs")
|
|
}
|
|
echo "${ret}"
|
|
}
|
|
|
|
uci_get_by_name() {
|
|
local ret=$(uci get dnscrypt-proxy.$1.$2 2>/dev/null)
|
|
echo "${ret:=$3}"
|
|
}
|
|
|
|
boot() {
|
|
dnscrypt_boot=1
|
|
rc_procd start_service
|
|
}
|
|
|
|
dnscrypt_instance() {
|
|
local config_path="$CONFIG_DIR/dnscrypt-proxy-$1.conf"
|
|
|
|
[ $(uci_get_by_name "$1" enable 0) -eq 1 ] || return 0
|
|
create_config_file $1 "$config_path"
|
|
|
|
procd_open_instance
|
|
procd_set_param command $PROG -config "$config_path" -pidfile "$SERVICE_PID_FILE"
|
|
procd_set_param file "$config_path"
|
|
procd_close_instance
|
|
}
|
|
|
|
include_for_firewall_reload() {
|
|
local FWI=$(uci get firewall.dnscrypt_proxy.path 2>/dev/null)
|
|
|
|
[ -n "$FWI" ] || return 0
|
|
echo '#!/bin/sh' >$FWI
|
|
cat <<-EOF >>"$FWI"
|
|
addr_filter=\$(uci get ${NAME}.ns1.addr_filter 2>/dev/null)
|
|
if [ "\${addr_filter:-auto}" == "auto" ]; then
|
|
for list in ${DIRECT_IPSETS}; do
|
|
ipset list -n "\${list}" >/dev/null 2>&1
|
|
[ \$? -eq 0 ] && addr_filter="\${list}" && break
|
|
done
|
|
fi
|
|
ipset list -n "\${addr_filter}" >/dev/null 2>&1 && {
|
|
ipset_shell=\$(find ${CONFIG_DIR} -name "dnscrypt_filter2_*.sh" | head -n1)
|
|
[ -z "\${ipset_shell}" ] || {
|
|
sed -i 's/^ipset add [^ ]* \(.*\)$/ipset add '"\${addr_filter}"' \1/g' "\${ipset_shell}"
|
|
echo "### \$(date) on firewall reload" >> "${LOG_FILE}.log"
|
|
echo "### appending resolver addresses to ipset list \${addr_filter}..." >> "${LOG_FILE}.log"
|
|
wc -l "\${ipset_shell}" >> "${LOG_FILE}.log" 2>&1
|
|
date >"\${ipset_shell}.log"
|
|
\${ipset_shell} >>"\${ipset_shell}.log" 2>&1 &
|
|
}
|
|
}
|
|
EOF
|
|
}
|
|
|
|
create_config_file() {
|
|
local server_names listen_address query_meta max_clients bootstrap_resolvers netprobe_address
|
|
local disabled_server_names log_file log_level timeout keepalive cert_refresh_delay netprobe_timeout
|
|
local log_files_max_size log_files_max_age log_files_max_backups reject_ttl
|
|
local reject_ttl cache_size cache_min_ttl cache_max_ttl cache_neg_min_ttl cache_neg_max_ttl
|
|
local lb_strategy blocked_query_response force_defaults force_gets
|
|
local proxy http_proxy cloaking_rules cloak_ttl
|
|
local config_path="$2"
|
|
local dstf two_line_header forward_resolvers
|
|
|
|
[ ! -d "$CONFIG_DIR" ] && mkdir -p "$CONFIG_DIR"
|
|
[ -d "${CACHE_DIR}" ] || mkdir -p "${CACHE_DIR}"
|
|
[ -f "$config_path" ] && rm "$config_path"
|
|
|
|
config_get server_names $1 'server_names' ''
|
|
config_get listen_addresses $1 'listen_addresses' '127.0.0.1:5335'
|
|
config_get query_meta $1 'query_meta' ''
|
|
config_get max_clients $1 'max_clients' '250'
|
|
config_get bootstrap_resolvers $1 'bootstrap_resolvers' '114.114.114.114:53'
|
|
config_get netprobe_address $1 'netprobe_address' '114.114.114.114:53'
|
|
config_get disabled_server_names $1 'disabled_server_names' ''
|
|
|
|
config_get log_file $1 'log_file' "${LOG_FILE}.log"
|
|
config_get log_level $1 'log_level' '2'
|
|
|
|
config_get timeout $1 'timeout' '5000'
|
|
config_get keepalive $1 'keepalive' '30'
|
|
config_get cert_refresh_delay $1 'cert_refresh_delay' '240'
|
|
config_get netprobe_timeout $1 'netprobe_timeout' '60'
|
|
config_get log_files_max_size $1 'log_files_max_size' '1'
|
|
config_get log_files_max_age $1 'log_files_max_age' '7'
|
|
config_get log_files_max_backups $1 'log_files_max_backups' '1'
|
|
config_get reject_ttl $1 'reject_ttl' '600'
|
|
config_get cache_size $1 'cache_size' '512'
|
|
config_get cache_min_ttl $1 'cache_min_ttl' '600'
|
|
config_get cache_max_ttl $1 'cache_max_ttl' '86400'
|
|
config_get cache_neg_min_ttl $1 'cache_neg_min_ttl' '60'
|
|
config_get cache_neg_max_ttl $1 'cache_neg_max_ttl' '600'
|
|
|
|
config_get lb_strategy $1 'lb_strategy' 'p2'
|
|
config_get blocked_query_response $1 'blocked_query_response' 'hinfo'
|
|
|
|
config_get proxy $1 'proxy' ''
|
|
config_get http_proxy $1 'http_proxy' ''
|
|
|
|
config_get cloak_ttl $1 'cloak_ttl' '600'
|
|
|
|
append_str_param "user_name" "$USER" $config_path
|
|
append_multivalue_param "server_names" "$server_names" $config_path
|
|
append_multivalue_param "listen_addresses" "$listen_addresses" $config_path
|
|
append_multivalue_param "query_meta" "$query_meta" $config_path
|
|
append_param "max_clients" "$max_clients" $config_path
|
|
append_multivalue_param "bootstrap_resolvers" "$bootstrap_resolvers" $config_path
|
|
append_str_param "netprobe_address" "$netprobe_address" $config_path
|
|
append_multivalue_param "disabled_server_names" "$disabled_server_names" $config_path
|
|
|
|
append_param "log_level" "$log_level" $config_path
|
|
append_str_param "log_file" "$log_file" $config_path
|
|
|
|
append_param "timeout" "$timeout" $config_path
|
|
append_param "cert_refresh_delay" "$cert_refresh_delay" $config_path
|
|
append_param "netprobe_timeout" "$netprobe_timeout" $config_path
|
|
append_param "log_files_max_size" "$log_files_max_size" $config_path
|
|
append_param "log_files_max_age" "$log_files_max_age" $config_path
|
|
append_param "log_files_max_backups" "$log_files_max_backups" $config_path
|
|
append_param "reject_ttl" "$reject_ttl" $config_path
|
|
append_param "cache_size" "$cache_size" $config_path
|
|
append_param "cache_min_ttl" "$cache_min_ttl" $config_path
|
|
append_param "cache_max_ttl" "$cache_max_ttl" $config_path
|
|
append_param "cache_neg_min_ttl" "$cache_neg_min_ttl" $config_path
|
|
append_param "cache_neg_max_ttl" "$cache_neg_max_ttl" $config_path
|
|
|
|
append_str_param "lb_strategy" "$lb_strategy" $config_path
|
|
append_str_param "blocked_query_response" "$blocked_query_response" $config_path
|
|
|
|
append_str_param "proxy" "$proxy" $config_path
|
|
append_str_param "http_proxy" "$http_proxy" $config_path
|
|
|
|
append_param "cloak_ttl" "$cloak_ttl" $config_path
|
|
|
|
force_defaults='
|
|
lb_estimator,true
|
|
ignore_system_dns,true
|
|
block_unqualified,true
|
|
block_undelegated,true
|
|
ipv4_servers,true
|
|
ipv6_servers,false
|
|
block_ipv6,true
|
|
dnscrypt_servers,true
|
|
doh_servers,true
|
|
require_dnssec,false
|
|
force_tcp,false
|
|
require_nolog,true
|
|
require_nofilter,true
|
|
cache,true
|
|
offline_mode,false
|
|
dnscrypt_ephemeral_keys,false
|
|
tls_disable_session_tickets,false
|
|
cert_ignore_timestamp,true
|
|
'
|
|
append_yes_no() {
|
|
local sets=$1
|
|
local defs=$2
|
|
local config_path=$3
|
|
local line param_name param_value val
|
|
|
|
for line in $defs; do
|
|
param_name="${line%%,*}"; param_value="${line##*,}";
|
|
for val in $sets; do
|
|
[[ x$val == x$param_name ]] && param_value=true
|
|
done
|
|
echo "$param_name = $param_value" >> $config_path
|
|
done
|
|
}
|
|
config_get force_gets $1 'force' ''
|
|
append_yes_no "$force_gets" "$force_defaults" "$config_path"
|
|
|
|
handle_list_file() {
|
|
local file
|
|
local fwd="$2"
|
|
if [ x"${1:0:4}" == x"http" ]; then file=$(cache_file "${1}" 0);
|
|
elif [ x"${1:0:1}" == x"/" ]; then file="${1}";
|
|
else file="$CACHE_DIR/${1}";
|
|
fi
|
|
[ x"$file" == x -o ! -f "${file}" ] && return
|
|
local tmpf="/tmp/dnscrypt_${file##*/}"
|
|
if [ x"${file##*.}" == xconf -o x"${file##*.}" == xadblock ]; then
|
|
sed -ne "s/^\(address\|server\)=\/\([^/]*\)\/.*$/\2 $fwd/p" -e "s/ *$//g" "$file" > "$tmpf"
|
|
else
|
|
[ -z "$fwd" ] || sed -i -e "s/$/ $fwd/g" "$tmpf"
|
|
cat "$file" > "$tmpf"
|
|
fi
|
|
echo "$tmpf"
|
|
}
|
|
|
|
append_list_file() {
|
|
local sec=$1
|
|
local list=$2
|
|
local param_name=$3
|
|
local txtf=$4
|
|
local log=$5
|
|
local config_path=$6
|
|
local line2=$7
|
|
local fwd=$8
|
|
|
|
echo -e $line2 > "$txtf"
|
|
[ -z "$log" ] || {
|
|
append_str_param "log_file" "${LOG_FILE}_$log" $config_path
|
|
append_str_param "log_format" "tsv" $config_path
|
|
}
|
|
merg_list_file() {
|
|
local src=${1%% *}
|
|
local list_file
|
|
|
|
list_file=$(handle_list_file $src "$fwd")
|
|
[[ x"$list_file" == x ]] && return 0
|
|
cat "$list_file" >> "$txtf"
|
|
}
|
|
config_list_foreach "$sec" "$list" merg_list_file $config_path
|
|
line=$(sort "$txtf" | uniq); echo "$line" > "$txtf"
|
|
line=$(cat "$txtf" | wc -l); [ $line -le 2 ] && rm -f "$txtf" && return
|
|
echo "#total $((line-1)) uniq items." >> "$txtf"
|
|
append_str_param "$param_name" "$txtf" $config_path
|
|
}
|
|
|
|
dstf="$CONFIG_DIR/dnscrypt_forwarding_rules.txt"
|
|
two_line_header='#dnscrypt forwarding rule file.\n'
|
|
forward_resolvers="$bootstrap_resolvers"
|
|
append_list_file $1 "forwarding_rules" "forwarding_rules" "$dstf" "" $config_path "$two_line_header" $forward_resolvers
|
|
|
|
dstf="$CONFIG_DIR/dnscrypt_cloaking_rules.txt"
|
|
two_line_header='#dnscrypt cloaking rules file.\n'
|
|
append_list_file $1 "cloaking_rules" "cloaking_rules" "$dstf" "" $config_path "$two_line_header"
|
|
|
|
echo "[query_log]" >> $config_path
|
|
append_str_param "file" "${LOG_FILE}_query.log" $config_path
|
|
append_str_param "format" "tsv" $config_path
|
|
|
|
echo "[nx_log]" >> $config_path
|
|
append_str_param "file" "${LOG_FILE}_nx.log" $config_path
|
|
append_str_param "format" "tsv" $config_path
|
|
|
|
echo "[blocked_names]" >> $config_path
|
|
dstf="$CONFIG_DIR/dnscrypt_blocked_names.txt"
|
|
two_line_header='#dnscrypt domain black list file.\n*.test\n*.onion\n*.localhost\n*.local\n*.invalid\n*.bind\n*.lan\n*.internal\n*.intranet\n*.private\n*.workgroup\n*.10.in-addr.arpa\n*.16.172.in-addr.arpa\n*.168.192.in-addr.arpa\n*.254.169.in-addr.arpa\n*.d.f.ip6.arpa'
|
|
append_list_file $1 "blocked_names" "blocked_names_file" "$dstf" "bd.log" $config_path "$two_line_header"
|
|
|
|
echo "[blocked_ips]" >> $config_path
|
|
dstf="$CONFIG_DIR/dnscrypt_blocked_ips.txt"
|
|
two_line_header='#dnscrypt ip black list file.\n127.*\n192.168.*'
|
|
append_list_file $1 "blocked_ips" "blocked_ips_file" "$dstf" "bi.log" $config_path "$two_line_header"
|
|
|
|
echo "[allowed_names]" >> $config_path
|
|
dstf="$CONFIG_DIR/dnscrypt_allowed_names.txt"
|
|
two_line_header='#dnscrypt white list file.\n'
|
|
append_list_file $1 "allowed_names" "allowed_names_file" "$dstf" "wd.log" $config_path "$two_line_header"
|
|
|
|
echo "[static]" >> $config_path
|
|
append_static_resolver() {
|
|
local sdns=$1
|
|
local config_path=$2
|
|
local sdns
|
|
|
|
[[ "x$sdns" == x ]] && return 0
|
|
echo "[static.'$sdns']" >> $config_path
|
|
append_str_param "sdns" "$sdns" $config_path
|
|
}
|
|
config_list_foreach "$1" "sdns" append_static_resolver $config_path
|
|
|
|
echo "[sources]" >> $config_path
|
|
append_resolvers_source() {
|
|
local resolver=$1
|
|
local config_path=$2
|
|
local section="global"
|
|
local urls cache_file minisign_key prefix selfsign details_json
|
|
|
|
config_unset $section "urls"
|
|
[ -f "/etc/config/${resolver}" ] || return 0
|
|
config_load ${resolver}
|
|
[ $? -eq 0 ] || return 0
|
|
config_get urls $section 'urls' ''
|
|
[[ "x$urls" == x ]] && return 0
|
|
echo "[sources.'${resolver}']" >> $config_path
|
|
|
|
config_get urls $section 'urls' ''
|
|
config_get format $section 'format' 'v2'
|
|
config_get cache_file $section 'cache_file' "${urls##*/}"
|
|
config_get minisign_key $section 'minisign_key' 'RWQf6LRCGA9i53mlYecO4IzT51TGPpvWucNSCh1CBM0QTaLn73Y7GFO3'
|
|
config_get prefix $section 'prefix' ''
|
|
|
|
config_get details_json $section 'details_json' "${cache_file%%.md}.json"
|
|
config_get_bool cache_mode $section 'cache_mode' '1'
|
|
config_get_bool selfsign $section 'selfsign' '0'
|
|
|
|
if [ $cache_mode -eq 1 ] ; then
|
|
local redirect_https site_addr schema port
|
|
redirect_https="$(uci_get uhttpd main redirect_https '0')"
|
|
site_addr="$(uci_get uhttpd main listen_https '[::]:80')"
|
|
schema=http
|
|
port=${site_addr##*:}
|
|
[ x"${port}" != "x80" -a $redirect_https -eq 1 ] && opkg list-installed | grep libustream-openssl && schema=https
|
|
[ x"${port}" == "x80" -o x"${port}" == "x443" ] && port=
|
|
urls="${schema}://localhost${port:+:${port}}/${urls##*/}"
|
|
fi
|
|
|
|
append_multivalue_param "urls" "$urls" $config_path
|
|
append_str_param "format" "$format" $config_path
|
|
append_str_param "cache_file" "$cache_file" $config_path
|
|
append_str_param "minisign_key" "$minisign_key" $config_path
|
|
append_str_param "prefix" "$prefix" $config_path
|
|
|
|
config_get_bool selfsign $section 'selfsign' '0'
|
|
config_get details_json $section 'details_json' ''
|
|
}
|
|
config_list_foreach "$1" "resolvers" append_resolvers_source $config_path
|
|
|
|
}
|
|
|
|
multivalue_parse() {
|
|
local param_values=$(echo $1 | sed -e 's/[, ]/\n/g' -e 's/\n\n/\n/g')
|
|
|
|
[[ x == "x${param_values}" ]] && echo -n "[]" && return 1
|
|
local p
|
|
p=$(for p in ${param_values}; do echo -n "'${p}'"; done)
|
|
echo -n "["
|
|
echo -n $p | sed -e "s/''/', '/g"
|
|
echo -n "]"
|
|
}
|
|
|
|
log_ignored_param() {
|
|
local param_name=$1
|
|
logger -t dnscrypt-proxy -p user.warn "dnscrypt-proxy plugins support not present, ignoring '$param_name' parameter."
|
|
}
|
|
|
|
append_multivalue_param() {
|
|
local param_name=$1
|
|
local param_value=$2
|
|
local config_path=$3
|
|
|
|
echo "$param_name = $(multivalue_parse $param_value)" >> $config_path
|
|
}
|
|
|
|
append_str_param() {
|
|
local param_name=$1
|
|
local param_value=$2
|
|
local config_path=$3
|
|
|
|
echo "$param_name = '$param_value'" >> $config_path
|
|
}
|
|
|
|
append_param() {
|
|
local param_name=$1
|
|
local param_value=$2
|
|
local config_path=$3
|
|
|
|
echo "$param_name = $param_value" >> $config_path
|
|
}
|
|
|
|
start_service() {
|
|
config_load dnscrypt-proxy
|
|
|
|
local addr_filter list
|
|
config_get addr_filter ns1 'addr_filter' 'auto'
|
|
if [ "${addr_filter}" == "auto" ]; then
|
|
for list in ${DIRECT_IPSETS}; do
|
|
ipset list -n "${list}" >/dev/null 2>&1
|
|
[ $? -eq 0 ] && addr_filter="${list}" && break
|
|
done
|
|
fi
|
|
ipset list -n "${addr_filter}" >/dev/null 2>&1 && {
|
|
local ipset_shell="${CONFIG_DIR}/dnscrypt_filter2_${addr_filter}.sh"
|
|
uci_filter_resolver_addrs > "${ipset_shell}"
|
|
sed -i 's/^\(.*\)$/ipset add '"${addr_filter}"' "\1" nomatch/g' "${ipset_shell}"
|
|
chmod +x "${ipset_shell}"
|
|
echo "### appending resolver addresses to ipset list ${addr_filter}..." >> "${LOG_FILE}.log"
|
|
wc -l "${ipset_shell}" >> "${LOG_FILE}.log" 2>&1
|
|
date >"${ipset_shell}.log"
|
|
${ipset_shell} >>"${ipset_shell}.log" 2>&1 &
|
|
}
|
|
include_for_firewall_reload
|
|
|
|
config_foreach dnscrypt_instance dnscrypt-proxy
|
|
}
|
|
|
|
service_triggers() {
|
|
procd_add_reload_trigger 'dnscrypt-proxy'
|
|
}
|
|
|
|
reload_service() {
|
|
stop
|
|
start
|
|
}
|
|
|
|
cache_file() {
|
|
local tdl=$1
|
|
local key=$2
|
|
local root=$3
|
|
local mode=${4:-0}
|
|
[ -n "$root" ] && tdl=$tdl$'\n'$tdl.minisig
|
|
for cdl in $tdl; do
|
|
echo "INFO: details[$cdl]" > $LUCI_STATUS && sleep 3
|
|
local md5=$(echo $cdl | md5sum | cut -d' ' -f1)
|
|
local fn="$CACHE_DIR/${md5}_${cdl##*/}"
|
|
local tmpf="/tmp/dnscrypt_${cdl##*/}.dl"
|
|
local line
|
|
[ ! -f "$fn" ] && {
|
|
wget -q --no-check-certificate -t 3 -T 30 -O "$tmpf" "$cdl"
|
|
[ $? -eq 0 ] || return
|
|
line=$(cat "$tmpf" | wc -l)
|
|
[ $line -gt 1 ] || return
|
|
cat /dev/null > "$fn"
|
|
[ -z "$key" -o $mode -eq 11 ] && echo "##source[#$line]: $cdl" >> "$fn"
|
|
cat "$tmpf" >> "$fn"
|
|
}
|
|
[ $mode -ge 10 ] && cat "$fn" > $root/${cdl##*/}
|
|
done
|
|
echo "${fn%%.minisig}"
|
|
}
|
|
|