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