1# $NetBSD: 20-resolv.conf,v 1.8 2015/08/21 10:39:00 roy Exp $
2
3# Generate /etc/resolv.conf
4# Support resolvconf(8) if available
5# We can merge other dhcpcd resolv.conf files into one like resolvconf,
6# but resolvconf is preferred as other applications like VPN clients
7# can readily hook into it.
8# Also, resolvconf can configure local nameservers such as bind
9# or dnsmasq. This is important as the libc resolver isn't that powerful.
10
11resolv_conf_dir="$state_dir/resolv.conf"
12NL="
13"
14
15build_resolv_conf()
16{
17	local cf="$state_dir/resolv.conf.$ifname"
18	local interfaces= header= search= srvs= servers= x=
19
20	# Build a list of interfaces
21	interfaces=$(list_interfaces "$resolv_conf_dir")
22
23	# Build the resolv.conf
24	if [ -n "$interfaces" ]; then
25		# Build the header
26		for x in ${interfaces}; do
27			header="$header${header:+, }$x"
28		done
29
30		# Build the search list
31		domain=$(cd "$resolv_conf_dir"; \
32			key_get_value "domain " ${interfaces})
33		search=$(cd "$resolv_conf_dir"; \
34			key_get_value "search " ${interfaces})
35		set -- ${domain}
36		domain="$1"
37		[ -n "$2" ] && search="$search $*"
38		[ -n "$search" ] && search="$(uniqify $search)"
39		[ "$domain" = "$search" ] && search=
40		[ -n "$domain" ] && domain="domain $domain$NL"
41		[ -n "$search" ] && search="search $search$NL"
42
43		# Build the nameserver list
44		srvs=$(cd "$resolv_conf_dir"; \
45			key_get_value "nameserver " ${interfaces})
46		for x in $(uniqify ${srvs}); do
47			servers="${servers}nameserver $x$NL"
48		done
49	fi
50	header="$signature_base${header:+ $from }$header"
51
52	# Assemble resolv.conf using our head and tail files
53	[ -f "$cf" ] && rm -f "$cf"
54	[ -d "$resolv_conf_dir" ] || mkdir -p "$resolv_conf_dir"
55	echo "$header" > "$cf"
56	if [ -f /etc/resolv.conf.head ]; then
57		cat /etc/resolv.conf.head >> "$cf"
58	else
59		echo "# /etc/resolv.conf.head can replace this line" >> "$cf"
60	fi
61	printf %s "$domain$search$servers" >> "$cf"
62	if [ -f /etc/resolv.conf.tail ]; then
63		cat /etc/resolv.conf.tail >> "$cf"
64	else
65		echo "# /etc/resolv.conf.tail can replace this line" >> "$cf"
66	fi
67	if change_file /etc/resolv.conf "$cf"; then
68		chmod 644 /etc/resolv.conf
69	fi
70	rm -f "$cf"
71}
72
73# Extract any ND DNS options from the RA
74# For now, we ignore the lifetime of the DNS options unless they
75# are absent or zero.
76# In this case they are removed from consideration.
77# See draft-gont-6man-slaac-dns-config-issues-01 for issues
78# regarding DNS option lifetime in ND messages.
79eval_nd_dns()
80{
81
82	eval ltime=\$nd${i}_rdnss${j}_lifetime
83	if [ -z "$ltime" -o "$ltime" = 0 ]; then
84		rdnss=
85	else
86		eval rdnss=\$nd${i}_rdnss${j}_servers
87	fi
88	eval ltime=\$nd${i}_dnssl${j}_lifetime
89	if [ -z "$ltime" -o "$ltime" = 0 ]; then
90		dnssl=
91	else
92		eval dnssl=\$nd${i}_dnssl${j}_search
93	fi
94
95	[ -z "$rdnss" -a -z "$dnssl" ] && return 1
96
97	new_rdnss="$new_rdnss${new_rdnss:+ }$rdnss"
98	new_dnssl="$new_dnssl${new_dnssl:+ }$dnssl"
99	j=$(($j + 1))
100	return 0
101}
102
103add_resolv_conf()
104{
105	local x= conf="$signature$NL" warn=true
106	local i j ltime rdnss dnssl new_rdnss new_dnssl
107
108	# Loop to extract the ND DNS options using our indexed shell values
109	i=1
110	j=1
111	while true; do
112		while true; do
113			eval_nd_dns || break
114		done
115		i=$(($i + 1))
116		j=1
117		eval_nd_dns || break
118	done
119	new_domain_name_servers="$new_domain_name_servers${new_domain_name_servers:+ }$new_rdnss"
120	new_domain_search="$new_domain_search${new_domain_search:+ }$new_dnssl"
121
122	# Derive a new domain from our various hostname options
123	if [ -z "$new_domain_name" ]; then
124		if [ "$new_dhcp6_fqdn" != "${new_dhcp6_fqdn#*.}" ]; then
125			new_domain_name="${new_dhcp6_fqdn#*.}"
126		elif [ "$new_fqdn" != "${new_fqdn#*.}" ]; then
127			new_domain_name="${new_fqdn#*.}"
128		elif [ "$new_host_name" != "${new_host_name#*.}" ]; then
129			new_domain_name="${new_host_name#*.}"
130		fi
131	fi
132
133	# If we don't have any configuration, remove it
134	if [ -z "$new_domain_name_servers" -a \
135		-z "$new_domain_name" -a \
136		-z "$new_domain_search" ]; then
137		remove_resolv_conf
138		return $?
139	fi
140
141	if [ -n "$new_domain_name" ]; then
142		set -- $new_domain_name
143		if valid_domainname "$1"; then
144			conf="${conf}domain $1$NL"
145		else
146			syslog err "Invalid domain name: $1"
147		fi
148		# If there is no search this, make this one
149		if [ -z "$new_domain_search" ]; then
150			new_domain_search="$new_domain_name"
151			[ "$new_domain_name" = "$1" ] && warn=true
152		fi
153	fi
154	if [ -n "$new_domain_search" ]; then
155		if valid_domainname_list $new_domain_search; then
156			conf="${conf}search $new_domain_search$NL"
157		elif ! $warn; then
158			syslog err "Invalid domain name in list:" \
159			    "$new_domain_search"
160		fi
161	fi
162	for x in ${new_domain_name_servers}; do
163		conf="${conf}nameserver $x$NL"
164	done
165	if type resolvconf >/dev/null 2>&1; then
166		[ -n "$ifmetric" ] && export IF_METRIC="$ifmetric"
167		printf %s "$conf" | resolvconf -a "$ifname"
168		return $?
169	fi
170
171	if [ -e "$resolv_conf_dir/$ifname" ]; then
172		rm -f "$resolv_conf_dir/$ifname"
173	fi
174	[ -d "$resolv_conf_dir" ] || mkdir -p "$resolv_conf_dir"
175	printf %s "$conf" > "$resolv_conf_dir/$ifname"
176	build_resolv_conf
177}
178
179remove_resolv_conf()
180{
181	if type resolvconf >/dev/null 2>&1; then
182		resolvconf -d "$ifname" -f
183	else
184		if [ -e "$resolv_conf_dir/$ifname" ]; then
185			rm -f "$resolv_conf_dir/$ifname"
186		fi
187		build_resolv_conf
188	fi
189}
190
191# For ease of use, map DHCP6 names onto our DHCP4 names
192case "$reason" in
193BOUND6|RENEW6|REBIND6|REBOOT6|INFORM6)
194	new_domain_name_servers="$new_dhcp6_name_servers"
195	new_domain_search="$new_dhcp6_domain_search"
196	;;
197esac
198
199if $if_up || [ "$reason" = ROUTERADVERT ]; then
200	add_resolv_conf
201elif $if_down; then
202	remove_resolv_conf
203fi
204