1 if [ ! "$_NETWORKING_RESOLV_SUBR" ]; then _NETWORKING_RESOLV_SUBR=1
3 # Copyright (c) 2006-2013 Devin Teske
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions
9 # 1. Redistributions of source code must retain the above copyright
10 # notice, this list of conditions and the following disclaimer.
11 # 2. Redistributions in binary form must reproduce the above copyright
12 # notice, this list of conditions and the following disclaimer in the
13 # documentation and/or other materials provided with the distribution.
15 # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 ############################################################ INCLUDES
31 BSDCFG_SHARE="/usr/share/bsdconfig"
32 . $BSDCFG_SHARE/common.subr || exit 1
33 f_dprintf "%s: loading includes..." networking/resolv.subr
34 f_include $BSDCFG_SHARE/dialog.subr
35 f_include $BSDCFG_SHARE/media/tcpip.subr
36 f_include $BSDCFG_SHARE/networking/common.subr
37 f_include $BSDCFG_SHARE/networking/ipaddr.subr
38 f_include $BSDCFG_SHARE/strings.subr
40 BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="120.networking"
41 f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr
43 ############################################################ CONFIGURATION
46 # When updating resolv.conf(5), should we populate the `search' directive with
47 # all possible sub-domains? In example, if the domain is "sub.domain.com", when
48 # the below option is set to 1, include both "sub.domain.com" and "domain.com"
49 # in the `search' directive, otherwise use only "sub.domain.com".
51 # When enabled (set to 1), specify the minimum number of dots required for each
52 # `search' domain by setting the second option below, `RESOLVER_SEARCH_NDOTS'.
54 : ${RESOLVER_SEARCH_DOMAINS_ALL:=1}
55 : ${RESOLVER_SEARCH_NDOTS:=1}
57 ############################################################ FUNCTIONS
59 # f_resolv_conf_domain
61 # Returns the domain configured in resolv.conf(5).
63 f_resolv_conf_domain()
65 tail -r "$RESOLV_CONF" 2> /dev/null | awk \
68 ( tolower($1) == "domain" ) \
78 # f_resolv_conf_search
80 # Returns the search configured in resolv.conf(5).
82 f_resolv_conf_search()
84 tail -r "$RESOLV_CONF" 2> /dev/null | awk \
89 if ( match(tl0, /^[[:space:]]*search[[:space:]]+/) ) {
90 search = substr($0, RLENGTH + 1)
91 sub(/[[:space:]]*#.*$/, "", search)
92 gsub(/[[:space:]]+/, " ", search)
102 # f_dialog_resolv_conf_update $hostname
104 # Updates the search/domain directives in resolv.conf(5) given a valid fully-
105 # qualified hostname.
107 # This function is a two-parter. Below is the awk(1) portion of the function,
108 # afterward is the sh(1) function which utilizes the below awk script.
110 f_dialog_resolv_conf_update_awk='
111 # Variables that should be defined on the invocation line:
113 # -v search_all="0|1"
114 # -v search_ndots="1+"
117 domain_found = search_found = 0
122 if ( search_ndots < 1 )
125 ndots = split(subdomain, labels, ".") - 1
126 while ( ndots-- >= search_ndots ) {
127 if ( length(search) ) search = search " "
128 search = search subdomain
129 sub(/[^.]*\./, "", subdomain)
135 if ( domain_found && search_found ) { print; next }
138 if ( ! domain_found && \
139 match(tl0, /^[[:space:]]*domain[[:space:]]+/) ) \
141 if ( length(domain) ) {
142 printf "%s%s\n", substr($0, 0, RLENGTH), domain
146 else if ( ! search_found && \
147 match(tl0, /^[[:space:]]*search[[:space:]]+/) ) \
149 if ( length(search) ) {
150 printf "%s%s\n", substr($0, 0, RLENGTH), search
157 if ( ! search_found && length(search) )
158 printf "search\t%s\n", search
159 if ( ! domain_found && length(domain) )
160 printf "domain\t%s\n", domain
163 f_dialog_resolv_conf_update()
165 local funcname=f_dialog_resolv_conf_update
169 # Extrapolate the desired domain search parameter for resolv.conf(5)
171 local search nfields ndots domain="${hostname#*.}"
172 if [ "$RESOLVER_SEARCH_DOMAINS_ALL" = "1" ]; then
174 IFS=. f_count_ifs nfields "$domain"
175 ndots=$(( $nfields - 1 ))
176 while [ $ndots -ge ${RESOLVER_SEARCH_NDOTS:-1} ]; do
177 search="$search $domain"
178 domain="${domain#*.}"
179 ndots=$(( $ndots - 1 ))
182 domain="${hostname#*.}"
188 # Save domain/search information only if different from resolv.conf(5)
190 if [ "$domain" != "$( f_resolv_conf_domain )" -o \
191 "$search" != "$( f_resolv_conf_search )" ]
193 f_dialog_info "Saving new domain/search settings" \
194 "to resolv.conf(5)..."
197 # Create a new temporary file to write our resolv.conf(5)
198 # update with our new `domain' and `search' directives.
201 f_eval_catch -dk tmpfile $funcname mktemp \
202 'mktemp -t "%s"' "$tmpfile" || return $DIALOG_CANCEL
205 # Fixup permissions and ownership (mktemp(1) creates the
206 # temporary file with 0600 permissions -- change the
207 # permissions and ownership to match resolv.conf(5) before
208 # we write it out and mv(1) it into place).
211 f_eval_catch -dk mode $funcname stat \
212 'stat -f "%%#Lp" "%s"' "$RESOLV_CONF" || mode=0644
213 f_eval_catch -dk owner $funcname stat \
214 'stat -f "%%u:%%g" "%s"' "$RESOLV_CONF" ||
216 f_eval_catch -d $funcname chmod \
217 'chmod "%s" "%s"' "$mode" "$tmpfile"
218 f_eval_catch -d $funcname chown \
219 'chown "%s" "%s"' "$owner" "$tmpfile"
222 # Operate on resolv.conf(5), replacing only the last
223 # occurrences of `domain' and `search' directives (or add
224 # them to the top if not found), in strict-adherence to the
225 # following entry in resolver(5):
227 # The domain and search keywords are mutually exclusive.
228 # If more than one instance of these keywords is present,
229 # the last instance will override.
231 # NOTE: If RESOLVER_SEARCH_DOMAINS_ALL is set to `1' in the
232 # environment, all sub-domains will be added to the `search'
233 # directive, not just the FQDN.
235 local domain="${hostname#*.}" new_contents
236 [ "$domain" = "$hostname" ] && domain=
237 new_contents=$( tail -r "$RESOLV_CONF" 2> /dev/null )
238 new_contents=$( echo "$new_contents" | awk \
239 -v domain="$domain" \
240 -v search_all="${RESOLVER_SEARCH_DOMAINS_ALL:-1}" \
241 -v search_ndots="${RESOLVER_SEARCH_NDOTS:-1}" \
242 "$f_dialog_resolv_conf_update_awk" )
245 # Write the temporary file contents and move the temporary
248 echo "$new_contents" | tail -r > "$tmpfile" ||
249 return $DIALOG_CANCEL
250 f_eval_catch -d $funcname mv \
251 'mv "%s" "%s"' "$tmpfile" "$RESOLV_CONF"
256 # f_dialog_input_nameserver [ $n $nameserver ]
258 # Allows the user to edit a given nameserver. The first argument is the
259 # resolv.conf(5) nameserver ``instance'' integer. For example, this will be one
260 # if editing the first nameserver instance, two if editing the second, three if
261 # the third, ad nauseum. If this argument is zero, null, or missing, the value
262 # entered by the user (if non-null) will be added to resolv.conf(5) as a new
263 # `nameserver' entry. The second argument is the IPv4 address of the nameserver
264 # to be edited -- this will be displayed as the initial value during the edit.
266 # Taint-checking is performed when editing an existing entry (when the second
267 # argument is one or higher) in that the first argument must match the current
268 # value of the Nth `nameserver' instance in resolv.conf(5) else an error is
269 # generated discarding any/all changes.
271 # This function is a two-parter. Below is the awk(1) portion of the function,
272 # afterward is the sh(1) function which utilizes the below awk script.
274 f_dialog_input_nameserver_edit_awk='
275 # Variables that should be defined on the invocation line:
281 if ( nsindex < 1 ) exit 1
285 if ( found ) { print; next }
287 if ( match(tolower($0), /^[[:space:]]*nameserver[[:space:]]+/)) {
288 if ( ++n == nsindex ) {
289 if ( $2 != old_value ) exit 2
290 if ( new_value != "" ) printf "%s%s\n", \
291 substr($0, 0, RLENGTH), new_value
298 END { if ( ! found ) exit 3 }
300 f_dialog_input_nameserver()
302 local funcname=f_dialog_input_nameserver
303 local index="${1:-0}" old_ns="$2" new_ns
307 # Perform sanity checks
309 f_isinteger "$index" || return $DIALOG_CANCEL
310 [ $index -ge 0 ] || return $DIALOG_CANCEL
313 if [ $index -gt 0 ]; then
314 if [ "$USE_XDIALOG" ]; then
315 msg="$xmsg_please_enter_nameserver_existing"
317 msg="$msg_please_enter_nameserver_existing"
320 msg="$msg_please_enter_nameserver"
324 # Loop until the user provides taint-free input.
327 f_dialog_input new_ns "$msg" "$ns" \
328 "$hline_num_punc_tab_enter" || return $?
330 # Take only the first "word" of the user's input
331 new_ns="${new_ns%%[$IFS]*}"
333 # Taint-check the user's input
334 [ "$new_ns" ] || break
335 f_dialog_validate_ipaddr "$new_ns" && break
337 # Update prompt to allow user to re-edit previous entry
342 # Save only if the user changed the nameserver.
344 if [ $index -eq "0" -a "$new_ns" ]; then
345 f_dialog_info "$msg_saving_nameserver"
346 printf "nameserver\t%s\n" "$new_ns" >> "$RESOLV_CONF"
348 elif [ $index -gt 0 -a "$old_ns" != "$new_ns" ]; then
349 if [ "$new_ns" ]; then
350 msg="$msg_saving_nameserver_existing"
352 msg="$msg_removing_nameserver"
357 # Create a new temporary file to write our new resolv.conf(5)
360 f_eval_catch -dk tmpfile $funcname mktemp \
361 'mktemp -t "%s"' "$pgm" || return $DIALOG_CANCEL
364 # Quietly fixup permissions and ownership
367 f_eval_catch -dk mode $funcname stat \
368 'stat -f "%%#Lp" "%s"' "$RESOLV_CONF" || mode=0644
369 f_eval_catch -dk owner $funcname stat \
370 'stat -f "%%u:%%g" "%s"' "$RESOLV_CONF" ||
372 f_eval_catch -d $funcname chmod \
373 'chmod "%s" "%s"' "$mode" "$tmpfile"
374 f_eval_catch -d $funcname chown \
375 'chown "%s" "%s"' "$owner" "$tmpfile"
378 # Operate on resolv.conf(5)
381 new_contents=$( awk -v nsindex="$index" \
382 -v old_value="$old_ns" \
383 -v new_value="$new_ns" \
384 "$f_dialog_input_nameserver_edit_awk" \
388 # Produce an appropriate error message if necessary.
392 1) f_die 1 "$msg_internal_error_nsindex_value" "$nsindex" ;;
393 2) f_show_msg "$msg_resolv_conf_changed_while_editing"
395 3) f_show_msg "$msg_resolv_conf_entry_no_longer_exists"
400 # Write the temporary file contents and move the temporary
403 echo "$new_contents" > "$tmpfile" || return $DIALOG_CANCEL
404 f_eval_catch -d $funcname mv \
405 'mv "%s" "%s"' "$tmpfile" "$RESOLV_CONF"
409 # f_dialog_menu_nameservers
411 # Edit the nameservers in resolv.conf(5).
413 f_dialog_menu_nameservers()
415 local prompt="$msg_dns_configuration"
416 local menu_list # Calculated below
417 local hline="$hline_arrows_tab_enter"
420 local height width rows
421 local opt_exit="$msg_return_to_previous_menu"
422 local opt_add="$msg_add_nameserver"
425 # Loop forever until the user has finished configuring nameservers
429 # Re/Build list of nameservers
432 f_resolv_conf_nameservers nameservers
436 echo "'X $msg_exit' '$opt_exit'"
437 index=$(( $index + 1 ))
439 echo "'A $msg_add' '$opt_add'"
440 index=$(( $index + 1 ))
442 for ns in $nameservers; do
443 [ $index -lt ${#DIALOG_MENU_TAGS} ] || break
444 f_substr -v tag "$DIALOG_MENU_TAGS" $index 1
445 echo "'$tag nameserver' '$ns'"
446 index=$(( $index + 1 ))
451 # Display configuration-edit menu
453 eval f_dialog_menu_size height width rows \
455 \"\$DIALOG_BACKTITLE\" \
460 tag=$( eval $DIALOG \
461 --title \"\$DIALOG_TITLE\" \
462 --backtitle \"\$DIALOG_BACKTITLE\" \
463 --hline \"\$hline\" \
464 --ok-label \"\$msg_ok\" \
465 --cancel-label \"\$msg_cancel\" \
466 --default-item \"\$defaultitem\" \
467 --menu \"\$prompt\" \
468 $height $width $rows \
470 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
473 f_dialog_data_sanitize tag
475 # Return if "Cancel" was chosen (-1) or ESC was pressed (255)
476 if [ $retval -ne $DIALOG_OK ]; then
479 # Only update default-item on success
484 "X $msg_exit") break ;;
486 f_dialog_input_nameserver
490 n=$( eval f_dialog_menutag2index \"\$tag\" $menu_list )
491 ns=$( eval f_dialog_menutag2item \"\$tag\" $menu_list )
492 f_dialog_input_nameserver $(( $n - 2 )) "$ns"
498 ############################################################ MAIN
500 f_dprintf "%s: Successfully loaded." networking/resolv.subr
502 fi # ! $_NETWORKING_RESOLV_SUBR