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"
10nocarrier_roaming_dir="$state_dir/roaming"
11NL="
12"
13: ${resolvconf:=resolvconf}
14if type "$resolvconf" >/dev/null 2>&1; then
15	have_resolvconf=true
16else
17	have_resolvconf=false
18fi
19
20build_resolv_conf()
21{
22	cf="$state_dir/resolv.conf.$ifname"
23
24	# Build a list of interfaces
25	interfaces=$(list_interfaces "$resolv_conf_dir")
26
27	# Build the resolv.conf
28	header=
29	if [ -n "$interfaces" ]; then
30		# Build the header
31		for x in ${interfaces}; do
32			header="$header${header:+, }$x"
33		done
34
35		# Build the search list
36		domain=$(cd "$resolv_conf_dir"; \
37			key_get_value "domain " ${interfaces})
38		search=$(cd "$resolv_conf_dir"; \
39			key_get_value "search " ${interfaces})
40		set -- ${domain}
41		domain="$1"
42		[ -n "$2" ] && search="$search $*"
43		[ -n "$search" ] && search="$(uniqify $search)"
44		[ "$domain" = "$search" ] && search=
45		[ -n "$domain" ] && domain="domain $domain$NL"
46		[ -n "$search" ] && search="search $search$NL"
47
48		# Build the nameserver list
49		srvs=$(cd "$resolv_conf_dir"; \
50			key_get_value "nameserver " ${interfaces})
51		for x in $(uniqify $srvs); do
52			servers="${servers}nameserver $x$NL"
53		done
54	fi
55	header="$signature_base${header:+ $from }$header"
56
57	# Assemble resolv.conf using our head and tail files
58	[ -f "$cf" ] && rm -f "$cf"
59	[ -d "$resolv_conf_dir" ] || mkdir -p "$resolv_conf_dir"
60	echo "$header" > "$cf"
61	if [ -f /etc/resolv.conf.head ]; then
62		cat /etc/resolv.conf.head >> "$cf"
63	else
64		echo "# /etc/resolv.conf.head can replace this line" >> "$cf"
65	fi
66	printf %s "$domain$search$servers" >> "$cf"
67	if [ -f /etc/resolv.conf.tail ]; then
68		cat /etc/resolv.conf.tail >> "$cf"
69	else
70		echo "# /etc/resolv.conf.tail can replace this line" >> "$cf"
71	fi
72	if change_file /etc/resolv.conf "$cf"; then
73		chmod 644 /etc/resolv.conf
74	fi
75	rm -f "$cf"
76}
77
78# Extract any ND DNS options from the RA
79# Obey the lifetimes
80eval_nd_dns()
81{
82
83	eval rdnsstime=\$nd${i}_rdnss${j}_lifetime
84	[ -z "$rdnsstime" ] && return 1
85	ltime=$(($rdnsstime - $offset))
86	if [ "$ltime" -gt 0 ]; then
87		eval rdnss=\$nd${i}_rdnss${j}_servers
88		[ -n "$rdnss" ] && new_rdnss="$new_rdnss${new_rdnss:+ }$rdnss"
89	fi
90
91	eval dnssltime=\$nd${i}_dnssl${j}_lifetime
92	[ -z "$dnssltime" ] && return 1
93	ltime=$(($dnssltime - $offset))
94	if [ "$ltime" -gt 0 ]; then
95		eval dnssl=\$nd${i}_dnssl${j}_search
96		[ -n "$dnssl" ] && new_dnssl="$new_dnssl${new_dnssl:+ }$dnssl"
97	fi
98
99	j=$(($j + 1))
100	return 0
101}
102
103add_resolv_conf()
104{
105	conf="$signature$NL"
106	warn=true
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		eval acquired=\$nd${i}_acquired
113		[ -z "$acquired" ] && break
114		eval now=\$nd${i}_now
115		[ -z "$now" ] && break
116		offset=$(($now - $acquired))
117		while true; do
118			eval_nd_dns || break
119		done
120		i=$(($i + 1))
121		j=1
122	done
123	[ -n "$new_rdnss" ] && \
124	    new_domain_name_servers="$new_domain_name_servers${new_domain_name_servers:+ }$new_rdnss"
125	[ -n "$new_dnssl" ] && \
126	    new_domain_search="$new_domain_search${new_domain_search:+ }$new_dnssl"
127
128	# Derive a new domain from our various hostname options
129	if [ -z "$new_domain_name" ]; then
130		if [ "$new_dhcp6_fqdn" != "${new_dhcp6_fqdn#*.}" ]; then
131			new_domain_name="${new_dhcp6_fqdn#*.}"
132		elif [ "$new_fqdn" != "${new_fqdn#*.}" ]; then
133			new_domain_name="${new_fqdn#*.}"
134		elif [ "$new_host_name" != "${new_host_name#*.}" ]; then
135			new_domain_name="${new_host_name#*.}"
136		fi
137	fi
138
139	# If we don't have any configuration, remove it
140	if [ -z "$new_domain_name_servers" ] &&
141	   [ -z "$new_domain_name" ] &&
142	   [ -z "$new_domain_search" ]; then
143		remove_resolv_conf
144		return $?
145	fi
146
147	if [ -n "$new_domain_name" ]; then
148		set -- $new_domain_name
149		if valid_domainname "$1"; then
150			conf="${conf}domain $1$NL"
151		else
152			syslog err "Invalid domain name: $1"
153		fi
154		# If there is no search this, make this one
155		if [ -z "$new_domain_search" ]; then
156			new_domain_search="$new_domain_name"
157			[ "$new_domain_name" = "$1" ] && warn=true
158		fi
159	fi
160	if [ -n "$new_domain_search" ]; then
161		new_domain_search=$(uniqify $new_domain_search)
162		if valid_domainname_list $new_domain_search; then
163			conf="${conf}search $new_domain_search$NL"
164		elif ! $warn; then
165			syslog err "Invalid domain name in list:" \
166			    "$new_domain_search"
167		fi
168	fi
169	new_domain_name_servers=$(uniqify $new_domain_name_servers)
170	for x in ${new_domain_name_servers}; do
171		conf="${conf}nameserver $x$NL"
172	done
173	if $have_resolvconf; then
174		[ -n "$ifmetric" ] && export IF_METRIC="$ifmetric"
175		printf %s "$conf" | "$resolvconf" -a "$ifname"
176		return $?
177	fi
178
179	if [ -e "$resolv_conf_dir/$ifname" ]; then
180		rm -f "$resolv_conf_dir/$ifname"
181	fi
182	[ -d "$resolv_conf_dir" ] || mkdir -p "$resolv_conf_dir"
183	printf %s "$conf" > "$resolv_conf_dir/$ifname"
184	build_resolv_conf
185}
186
187remove_resolv_conf()
188{
189	if $have_resolvconf; then
190		"$resolvconf" -d "$ifname" -f
191	else
192		if [ -e "$resolv_conf_dir/$ifname" ]; then
193			rm -f "$resolv_conf_dir/$ifname"
194		fi
195		build_resolv_conf
196	fi
197}
198
199# For ease of use, map DHCP6 names onto our DHCP4 names
200case "$reason" in
201BOUND6|RENEW6|REBIND6|REBOOT6|INFORM6)
202	new_domain_name_servers="$new_dhcp6_name_servers"
203	new_domain_search="$new_dhcp6_domain_search"
204	;;
205esac
206
207if $if_configured; then
208	if $have_resolvconf && [ "$reason" = NOCARRIER_ROAMING ]; then
209		# avoid calling resolvconf -c on CARRIER unless we roam
210		mkdir -p "$nocarrier_roaming_dir"
211		echo " " >"$nocarrier_roaming_dir/$interface"
212		"$resolvconf" -C "$interface.*"
213	elif $have_resolvconf && [ "$reason" = CARRIER ]; then
214		# Not all resolvconf implementations support -c
215		if [ -e "$nocarrier_roaming_dir/$interface" ]; then
216			rm -f "$nocarrier_roaming_dir/$interface"
217			"$resolvconf" -c "$interface.*"
218		fi
219	elif $if_up || [ "$reason" = ROUTERADVERT ]; then
220		add_resolv_conf
221	elif $if_down; then
222		remove_resolv_conf
223	fi
224fi
225