From 57b86b69a24fb9feb0dd3a56f00c50ebd363e2e6 Mon Sep 17 00:00:00 2001 From: Austin Gilmour Date: Sun, 21 Jun 2026 10:14:37 -0400 Subject: [PATCH] fix(dns): support N upstream DNS servers in custom DNS setup Replace PIHOLE_DNS_1/PIHOLE_DNS_2 numbered variables with a bash array PIHOLE_DNS=() throughout basic-install.sh. This naturally supports one or more upstream DNS entries without duplicating a single entry into both slots when no comma is present in the input. - User input is split on commas into the array; empty entries skipped - Invalid entries are filtered out; valid ones retained - Confirmation dialog joins array elements so it reflects exactly what was entered - Preset DNS selection populates the array from the semicolon-delimited preset string, adding a second element only when one is present Closes #4783. Signed-off-by: Austin Gilmour --- automated install/basic-install.sh | 140 ++++++++++++++++++++--------- 1 file changed, 97 insertions(+), 43 deletions(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index eaee8747..c22c5d1f 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -875,19 +875,16 @@ setDNS() { until [[ "${DNSSettingsCorrect}" = True ]]; do # Signal value, to be used if the user inputs an invalid IP address strInvalid="Invalid" - if [[ ! "${PIHOLE_DNS_1}" ]]; then - if [[ ! "${PIHOLE_DNS_2}" ]]; then - # If the first and second upstream servers do not exist, do not prepopulate an IP address - prePopulate="" - else - # Otherwise, prepopulate the dialogue with the appropriate DNS value(s) - prePopulate=", ${PIHOLE_DNS_2}" - fi - elif [[ "${PIHOLE_DNS_1}" ]] && [[ ! "${PIHOLE_DNS_2}" ]]; then - prePopulate="${PIHOLE_DNS_1}" - elif [[ "${PIHOLE_DNS_1}" ]] && [[ "${PIHOLE_DNS_2}" ]]; then - prePopulate="${PIHOLE_DNS_1}, ${PIHOLE_DNS_2}" - fi + # Prepopulate the dialog with any previously set DNS values + prePopulate="" + i=1 + while true; do + varname="PIHOLE_DNS_${i}" + [[ -n "${!varname}" ]] || break + [[ -n "${prePopulate}" ]] && prePopulate+=", " + prePopulate+="${!varname}" + ((i++)) + done # Prompt the user to enter custom upstream servers piholeDNS=$(dialog --no-shadow --keep-tite --output-fd 1 \ @@ -906,45 +903,93 @@ If you want to specify a port other than 53, separate it with a hash.\ ;; esac - # Clean user input and replace whitespace with comma. + # Clean user input: replace whitespace with comma, then collapse consecutive commas. piholeDNS="${piholeDNS//[[:blank:]]/,}" + while [[ "${piholeDNS}" == *,,* ]]; do piholeDNS="${piholeDNS//,,/,}"; done + piholeDNS="${piholeDNS#,}" + piholeDNS="${piholeDNS%,}" - # Separate the user input into the two DNS values (separated by a comma) - printf -v PIHOLE_DNS_1 "%s" "${piholeDNS%%,*}" - printf -v PIHOLE_DNS_2 "%s" "${piholeDNS##*,}" + # Clear any previously set PIHOLE_DNS_N variables before re-assigning. + i=1 + while true; do + varname="PIHOLE_DNS_${i}" + [[ -n "${!varname}" ]] || break + unset "${varname}" + ((i++)) + done - # If the first DNS value is invalid (neither IPv4 nor IPv6) or empty, set PIHOLE_DNS_1="Invalid" - if ! valid_ip "${PIHOLE_DNS_1}" && ! valid_ip6 "${PIHOLE_DNS_1}" || [[ -z "${PIHOLE_DNS_1}" ]]; then - PIHOLE_DNS_1=${strInvalid} + # Split the cleaned input on commas and assign each entry to PIHOLE_DNS_N. + IFS=',' read -ra dns_array <<< "${piholeDNS}" + i=1 + for entry in "${dns_array[@]}"; do + [[ -z "${entry}" ]] && continue + printf -v "PIHOLE_DNS_${i}" "%s" "${entry}" + ((i++)) + done + + # Validate each DNS entry; build a list of invalid values. + invalidList="" + i=1 + while true; do + varname="PIHOLE_DNS_${i}" + val="${!varname}" + [[ -n "${val}" ]] || break + if ! valid_ip "${val}" && ! valid_ip6 "${val}"; then + printf -v "${varname}" "%s" "${strInvalid}" + invalidList+=" ${val}," + fi + ((i++)) + done + # Require at least one DNS server to have been entered. + if [[ -z "${PIHOLE_DNS_1}" ]]; then + PIHOLE_DNS_1="${strInvalid}" + invalidList+=" (none)," fi - # If the second DNS value is invalid but not empty, set PIHOLE_DNS_2="Invalid" - if ! valid_ip "${PIHOLE_DNS_2}" && ! valid_ip6 "${PIHOLE_DNS_2}" && [[ -n "${PIHOLE_DNS_2}" ]]; then - PIHOLE_DNS_2=${strInvalid} - fi - # If either of the DNS servers are invalid, - if [[ "${PIHOLE_DNS_1}" == "${strInvalid}" ]] || [[ "${PIHOLE_DNS_2}" == "${strInvalid}" ]]; then - # explain this to the user, + # Remove trailing comma from list. + invalidList="${invalidList%,}" + + # If any entries were invalid, explain to the user and retry. + if [[ -n "${invalidList}" ]]; then dialog --no-shadow --keep-tite \ --title "Invalid IP Address(es)" \ --backtitle "Invalid IP" \ - --msgbox "\\nOne or both of the entered IP addresses were invalid. Please try again.\ -\\n\\nInvalid IPs: ${PIHOLE_DNS_1}, ${PIHOLE_DNS_2}" \ + --msgbox "\\nOne or more of the entered IP addresses were invalid. Please try again.\ +\\n\\nInvalid IPs:${invalidList}" \ "${r}" "${c}" - # set the variables back to nothing, - if [[ "${PIHOLE_DNS_1}" == "${strInvalid}" ]]; then - PIHOLE_DNS_1="" - fi - if [[ "${PIHOLE_DNS_2}" == "${strInvalid}" ]]; then - PIHOLE_DNS_2="" - fi - # and continue the loop. + # Keep valid entries and discard invalid ones, then re-compact the numbering. + valid_entries=() + i=1 + while true; do + varname="PIHOLE_DNS_${i}" + val="${!varname}" + [[ -n "${val}" ]] || break + if [[ "${val}" != "${strInvalid}" ]]; then + valid_entries+=("${val}") + fi + unset "${varname}" + ((i++)) + done + for i in "${!valid_entries[@]}"; do + printf -v "PIHOLE_DNS_$((i + 1))" "%s" "${valid_entries[${i}]}" + done + DNSSettingsCorrect=False else + # Build confirmation message listing every DNS server. + local confirmDNSMsg="Are these settings correct?\\n" + i=1 + while true; do + varname="PIHOLE_DNS_${i}" + val="${!varname}" + [[ -n "${val}" ]] || break + confirmDNSMsg+=$'\t'"DNS Server ${i}:"$'\t'"${val}\\n" + ((i++)) + done dialog --no-shadow --no-collapse --keep-tite \ --backtitle "Specify Upstream DNS Provider(s)" \ --title "Upstream DNS Provider(s)" \ - --yesno "Are these settings correct?\\n"$'\t'"DNS Server 1:"$'\t'"${PIHOLE_DNS_1}\\n"$'\t'"DNS Server 2:"$'\t'"${PIHOLE_DNS_2}" \ + --yesno "${confirmDNSMsg}" \ "${r}" "${c}" && result=0 || result=$? case ${result} in @@ -2426,11 +2471,20 @@ main() { # needs to be done after FTL service has been started, otherwise pihole.toml does not exist # set on fresh installations by setDNS() and setPrivacyLevel() and setLogging() - # Upstreams may be needed in order to run gravity.sh - if [ -n "${PIHOLE_DNS_1}" ]; then - local string="\"${PIHOLE_DNS_1}\"" - [ -n "${PIHOLE_DNS_2}" ] && string+=", \"${PIHOLE_DNS_2}\"" - setFTLConfigValue "dns.upstreams" "[ ${string} ]" + # Upstreams may be needed in order to run gravity.sh. + # Build the dns.upstreams array from all PIHOLE_DNS_N variables. + local dns_strings="" + i=1 + while true; do + varname="PIHOLE_DNS_${i}" + val="${!varname}" + [[ -n "${val}" ]] || break + [[ -n "${dns_strings}" ]] && dns_strings+=", " + dns_strings+="\"${val}\"" + ((i++)) + done + if [ -n "${dns_strings}" ]; then + setFTLConfigValue "dns.upstreams" "[ ${dns_strings} ]" fi if [ -n "${QUERY_LOGGING}" ]; then