#!/bin/sh /etc/rc.common # # Copyright (C) 2019 p.t. # # 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}" }