diff options
Diffstat (limited to 'bsdconfig/networking/share/resolv.subr')
-rw-r--r-- | bsdconfig/networking/share/resolv.subr | 502 |
1 files changed, 502 insertions, 0 deletions
diff --git a/bsdconfig/networking/share/resolv.subr b/bsdconfig/networking/share/resolv.subr new file mode 100644 index 000000000000..779863c9981d --- /dev/null +++ b/bsdconfig/networking/share/resolv.subr @@ -0,0 +1,502 @@ +if [ ! "$_NETWORKING_RESOLV_SUBR" ]; then _NETWORKING_RESOLV_SUBR=1 +# +# Copyright (c) 2006-2013 Devin Teske +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_SHARE="/usr/share/bsdconfig" +. $BSDCFG_SHARE/common.subr || exit 1 +f_dprintf "%s: loading includes..." networking/resolv.subr +f_include $BSDCFG_SHARE/dialog.subr +f_include $BSDCFG_SHARE/media/tcpip.subr +f_include $BSDCFG_SHARE/networking/common.subr +f_include $BSDCFG_SHARE/networking/ipaddr.subr +f_include $BSDCFG_SHARE/strings.subr + +BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="120.networking" +f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr + +############################################################ CONFIGURATION + +# +# When updating resolv.conf(5), should we populate the `search' directive with +# all possible sub-domains? In example, if the domain is "sub.domain.com", when +# the below option is set to 1, include both "sub.domain.com" and "domain.com" +# in the `search' directive, otherwise use only "sub.domain.com". +# +# When enabled (set to 1), specify the minimum number of dots required for each +# `search' domain by setting the second option below, `RESOLVER_SEARCH_NDOTS'. +# +: ${RESOLVER_SEARCH_DOMAINS_ALL:=1} +: ${RESOLVER_SEARCH_NDOTS:=1} + +############################################################ FUNCTIONS + +# f_resolv_conf_domain +# +# Returns the domain configured in resolv.conf(5). +# +f_resolv_conf_domain() +{ + tail -r "$RESOLV_CONF" 2> /dev/null | awk \ + ' + BEGIN { found = 0 } + ( tolower($1) == "domain" ) \ + { + print $2 + found = 1 + exit + } + END { exit ! found } + ' +} + +# f_resolv_conf_search +# +# Returns the search configured in resolv.conf(5). +# +f_resolv_conf_search() +{ + tail -r "$RESOLV_CONF" 2> /dev/null | awk \ + ' + BEGIN { found = 0 } + { + tl0 = tolower($0) + if ( match(tl0, /^[[:space:]]*search[[:space:]]+/) ) { + search = substr($0, RLENGTH + 1) + sub(/[[:space:]]*#.*$/, "", search) + gsub(/[[:space:]]+/, " ", search) + print search + found = 1 + exit + } + } + END { exit ! found } + ' +} + +# f_dialog_resolv_conf_update $hostname +# +# Updates the search/domain directives in resolv.conf(5) given a valid fully- +# qualified hostname. +# +# This function is a two-parter. Below is the awk(1) portion of the function, +# afterward is the sh(1) function which utilizes the below awk script. +# +f_dialog_resolv_conf_update_awk=' +# Variables that should be defined on the invocation line: +# -v domain="domain" +# -v search_all="0|1" +# -v search_ndots="1+" +# +BEGIN { + domain_found = search_found = 0 + + if ( search_all ) { + search = "" + subdomain = domain + if ( search_ndots < 1 ) + search_ndots = 1 + + ndots = split(subdomain, labels, ".") - 1 + while ( ndots-- >= search_ndots ) { + if ( length(search) ) search = search " " + search = search subdomain + sub(/[^.]*\./, "", subdomain) + } + } + else search = domain +} +{ + if ( domain_found && search_found ) { print; next } + + tl0 = tolower($0) + if ( ! domain_found && \ + match(tl0, /^[[:space:]]*domain[[:space:]]+/) ) \ + { + if ( length(domain) ) { + printf "%s%s\n", substr($0, 0, RLENGTH), domain + domain_found = 1 + } + } + else if ( ! search_found && \ + match(tl0, /^[[:space:]]*search[[:space:]]+/) ) \ + { + if ( length(search) ) { + printf "%s%s\n", substr($0, 0, RLENGTH), search + search_found = 1 + } + } + else print +} +END { + if ( ! search_found && length(search) ) + printf "search\t%s\n", search + if ( ! domain_found && length(domain) ) + printf "domain\t%s\n", domain +} +' +f_dialog_resolv_conf_update() +{ + local funcname=f_dialog_resolv_conf_update + local hostname="$1" + + # + # Extrapolate the desired domain search parameter for resolv.conf(5) + # + local search nfields ndots domain="${hostname#*.}" + if [ "$RESOLVER_SEARCH_DOMAINS_ALL" = "1" ]; then + search= + IFS=. f_count_ifs nfields "$domain" + ndots=$(( $nfields - 1 )) + while [ $ndots -ge ${RESOLVER_SEARCH_NDOTS:-1} ]; do + search="$search $domain" + domain="${domain#*.}" + ndots=$(( $ndots - 1 )) + done + search="${search# }" + domain="${hostname#*.}" + else + search="$domain" + fi + + # + # Save domain/search information only if different from resolv.conf(5) + # + if [ "$domain" != "$( f_resolv_conf_domain )" -o \ + "$search" != "$( f_resolv_conf_search )" ] + then + f_dialog_info "Saving new domain/search settings" \ + "to resolv.conf(5)..." + + # + # Create a new temporary file to write our resolv.conf(5) + # update with our new `domain' and `search' directives. + # + local tmpfile + f_eval_catch -dk tmpfile $funcname mktemp \ + 'mktemp -t "%s"' "$tmpfile" || return $DIALOG_CANCEL + + # + # Fixup permissions and ownership (mktemp(1) creates the + # temporary file with 0600 permissions -- change the + # permissions and ownership to match resolv.conf(5) before + # we write it out and mv(1) it into place). + # + local mode owner + f_eval_catch -dk mode $funcname stat \ + 'stat -f "%%#Lp" "%s"' "$RESOLV_CONF" || mode=0644 + f_eval_catch -dk owner $funcname stat \ + 'stat -f "%%u:%%g" "%s"' "$RESOLV_CONF" || + owner="root:wheel" + f_eval_catch -d $funcname chmod \ + 'chmod "%s" "%s"' "$mode" "$tmpfile" + f_eval_catch -d $funcname chown \ + 'chown "%s" "%s"' "$owner" "$tmpfile" + + # + # Operate on resolv.conf(5), replacing only the last + # occurrences of `domain' and `search' directives (or add + # them to the top if not found), in strict-adherence to the + # following entry in resolver(5): + # + # The domain and search keywords are mutually exclusive. + # If more than one instance of these keywords is present, + # the last instance will override. + # + # NOTE: If RESOLVER_SEARCH_DOMAINS_ALL is set to `1' in the + # environment, all sub-domains will be added to the `search' + # directive, not just the FQDN. + # + local domain="${hostname#*.}" new_contents + [ "$domain" = "$hostname" ] && domain= + new_contents=$( tail -r "$RESOLV_CONF" 2> /dev/null ) + new_contents=$( echo "$new_contents" | awk \ + -v domain="$domain" \ + -v search_all="${RESOLVER_SEARCH_DOMAINS_ALL:-1}" \ + -v search_ndots="${RESOLVER_SEARCH_NDOTS:-1}" \ + "$f_dialog_resolv_conf_update_awk" ) + + # + # Write the temporary file contents and move the temporary + # file into place. + # + echo "$new_contents" | tail -r > "$tmpfile" || + return $DIALOG_CANCEL + f_eval_catch -d $funcname mv \ + 'mv "%s" "%s"' "$tmpfile" "$RESOLV_CONF" + + fi +} + +# f_dialog_input_nameserver [ $n $nameserver ] +# +# Allows the user to edit a given nameserver. The first argument is the +# resolv.conf(5) nameserver ``instance'' integer. For example, this will be one +# if editing the first nameserver instance, two if editing the second, three if +# the third, ad nauseum. If this argument is zero, null, or missing, the value +# entered by the user (if non-null) will be added to resolv.conf(5) as a new +# `nameserver' entry. The second argument is the IPv4 address of the nameserver +# to be edited -- this will be displayed as the initial value during the edit. +# +# Taint-checking is performed when editing an existing entry (when the second +# argument is one or higher) in that the first argument must match the current +# value of the Nth `nameserver' instance in resolv.conf(5) else an error is +# generated discarding any/all changes. +# +# This function is a two-parter. Below is the awk(1) portion of the function, +# afterward is the sh(1) function which utilizes the below awk script. +# +f_dialog_input_nameserver_edit_awk=' +# Variables that should be defined on the invocation line: +# -v nsindex="1+" +# -v old_value="..." +# -v new_value="..." +# +BEGIN { + if ( nsindex < 1 ) exit 1 + found = n = 0 +} +{ + if ( found ) { print; next } + + if ( match(tolower($0), /^[[:space:]]*nameserver[[:space:]]+/)) { + if ( ++n == nsindex ) { + if ( $2 != old_value ) exit 2 + if ( new_value != "" ) printf "%s%s\n", \ + substr($0, 0, RLENGTH), new_value + found = 1 + } + else print + } + else print +} +END { if ( ! found ) exit 3 } +' +f_dialog_input_nameserver() +{ + local funcname=f_dialog_input_nameserver + local index="${1:-0}" old_ns="$2" new_ns + local ns="$old_ns" + + # + # Perform sanity checks + # + f_isinteger "$index" || return $DIALOG_CANCEL + [ $index -ge 0 ] || return $DIALOG_CANCEL + + local msg + if [ $index -gt 0 ]; then + if [ "$USE_XDIALOG" ]; then + msg="$xmsg_please_enter_nameserver_existing" + else + msg="$msg_please_enter_nameserver_existing" + fi + else + msg="$msg_please_enter_nameserver" + fi + + # + # Loop until the user provides taint-free input. + # + while :; do + f_dialog_input new_ns "$msg" "$ns" \ + "$hline_num_punc_tab_enter" || return $? + + # Take only the first "word" of the user's input + new_ns="${new_ns%%[$IFS]*}" + + # Taint-check the user's input + [ "$new_ns" ] || break + f_dialog_validate_ipaddr "$new_ns" && break + + # Update prompt to allow user to re-edit previous entry + ns="$new_ns" + done + + # + # Save only if the user changed the nameserver. + # + if [ $index -eq "0" -a "$new_ns" ]; then + f_dialog_info "$msg_saving_nameserver" + printf "nameserver\t%s\n" "$new_ns" >> "$RESOLV_CONF" + return $DIALOG_OK + elif [ $index -gt 0 -a "$old_ns" != "$new_ns" ]; then + if [ "$new_ns" ]; then + msg="$msg_saving_nameserver_existing" + else + msg="$msg_removing_nameserver" + fi + f_dialog_info "$msg" + + # + # Create a new temporary file to write our new resolv.conf(5) + # + local tmpfile + f_eval_catch -dk tmpfile $funcname mktemp \ + 'mktemp -t "%s"' "$pgm" || return $DIALOG_CANCEL + + # + # Quietly fixup permissions and ownership + # + local mode owner + f_eval_catch -dk mode $funcname stat \ + 'stat -f "%%#Lp" "%s"' "$RESOLV_CONF" || mode=0644 + f_eval_catch -dk owner $funcname stat \ + 'stat -f "%%u:%%g" "%s"' "$RESOLV_CONF" || + owner="root:wheel" + f_eval_catch -d $funcname chmod \ + 'chmod "%s" "%s"' "$mode" "$tmpfile" + f_eval_catch -d $funcname chown \ + 'chown "%s" "%s"' "$owner" "$tmpfile" + + # + # Operate on resolv.conf(5) + # + local new_contents + new_contents=$( awk -v nsindex="$index" \ + -v old_value="$old_ns" \ + -v new_value="$new_ns" \ + "$f_dialog_input_nameserver_edit_awk" \ + "$RESOLV_CONF" ) + + # + # Produce an appropriate error message if necessary. + # + local retval=$? + case $retval in + 1) f_die 1 "$msg_internal_error_nsindex_value" "$nsindex" ;; + 2) f_show_msg "$msg_resolv_conf_changed_while_editing" + return $retval ;; + 3) f_show_msg "$msg_resolv_conf_entry_no_longer_exists" + return $retval ;; + esac + + # + # Write the temporary file contents and move the temporary + # file into place. + # + echo "$new_contents" > "$tmpfile" || return $DIALOG_CANCEL + f_eval_catch -d $funcname mv \ + 'mv "%s" "%s"' "$tmpfile" "$RESOLV_CONF" + fi +} + +# f_dialog_menu_nameservers +# +# Edit the nameservers in resolv.conf(5). +# +f_dialog_menu_nameservers() +{ + local prompt="$msg_dns_configuration" + local menu_list # Calculated below + local hline="$hline_arrows_tab_enter" + local defaultitem= + + local height width rows + local opt_exit="$msg_return_to_previous_menu" + local opt_add="$msg_add_nameserver" + + # + # Loop forever until the user has finished configuring nameservers + # + while :; do + # + # Re/Build list of nameservers + # + local nameservers + f_resolv_conf_nameservers nameservers + menu_list=$( + index=1 + + echo "'X $msg_exit' '$opt_exit'" + index=$(( $index + 1 )) + + echo "'A $msg_add' '$opt_add'" + index=$(( $index + 1 )) + + for ns in $nameservers; do + [ $index -lt ${#DIALOG_MENU_TAGS} ] || break + f_substr -v tag "$DIALOG_MENU_TAGS" $index 1 + echo "'$tag nameserver' '$ns'" + index=$(( $index + 1 )) + done + ) + + # + # Display configuration-edit menu + # + eval f_dialog_menu_size height width rows \ + \"\$DIALOG_TITLE\" \ + \"\$DIALOG_BACKTITLE\" \ + \"\$prompt\" \ + \"\$hline\" \ + $menu_list + local tag + tag=$( eval $DIALOG \ + --title \"\$DIALOG_TITLE\" \ + --backtitle \"\$DIALOG_BACKTITLE\" \ + --hline \"\$hline\" \ + --ok-label \"\$msg_ok\" \ + --cancel-label \"\$msg_cancel\" \ + --default-item \"\$defaultitem\" \ + --menu \"\$prompt\" \ + $height $width $rows \ + $menu_list \ + 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD + ) + local retval=$? + f_dialog_data_sanitize tag + + # Return if "Cancel" was chosen (-1) or ESC was pressed (255) + if [ $retval -ne $DIALOG_OK ]; then + return $retval + else + # Only update default-item on success + defaultitem="$tag" + fi + + case "$tag" in + "X $msg_exit") break ;; + "A $msg_add") + f_dialog_input_nameserver + ;; + *) + local n ns + n=$( eval f_dialog_menutag2index \"\$tag\" $menu_list ) + ns=$( eval f_dialog_menutag2item \"\$tag\" $menu_list ) + f_dialog_input_nameserver $(( $n - 2 )) "$ns" + ;; + esac + done +} + +############################################################ MAIN + +f_dprintf "%s: Successfully loaded." networking/resolv.subr + +fi # ! $_NETWORKING_RESOLV_SUBR |