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	header=
23	if [ -n "$interfaces" ]; then
24		# Build the header
25		for x in ${interfaces}; do
26			header="$header${header:+, }$x"
27		done
28
29		# Build the search list
30		domain=$(cd "$resolv_conf_dir"; \
31			key_get_value "domain " ${interfaces})
32		search=$(cd "$resolv_conf_dir"; \
33			key_get_value "search " ${interfaces})
34		set -- ${domain}
35		domain="$1"
36		[ -n "$2" ] && search="$search $*"
37		[ -n "$search" ] && search="$(uniqify $search)"
38		[ "$domain" = "$search" ] && search=
39		[ -n "$domain" ] && domain="domain $domain$NL"
40		[ -n "$search" ] && search="search $search$NL"
41
42		# Build the nameserver list
43		srvs=$(cd "$resolv_conf_dir"; \
44			key_get_value "nameserver " ${interfaces})
45		for x in $(uniqify $srvs); do
46			servers="${servers}nameserver $x$NL"
47		done
48	fi
49	header="$signature_base${header:+ $from }$header"
50
51	# Assemble resolv.conf using our head and tail files
52	[ -f "$cf" ] && rm -f "$cf"
53	[ -d "$resolv_conf_dir" ] || mkdir -p "$resolv_conf_dir"
54	echo "$header" > "$cf"
55	if [ -f /etc/resolv.conf.head ]; then
56		cat /etc/resolv.conf.head >> "$cf"
57	else
58		echo "# /etc/resolv.conf.head can replace this line" >> "$cf"
59	fi
60	printf %s "$domain$search$servers" >> "$cf"
61	if [ -f /etc/resolv.conf.tail ]; then
62		cat /etc/resolv.conf.tail >> "$cf"
63	else
64		echo "# /etc/resolv.conf.tail can replace this line" >> "$cf"
65	fi
66	if change_file /etc/resolv.conf "$cf"; then
67		chmod 644 /etc/resolv.conf
68	fi
69	rm -f "$cf"
70}
71
72# Extract any ND DNS options from the RA
73# Obey the lifetimes
74eval_nd_dns()
75{
76
77	eval rdnsstime=\$nd${i}_rdnss${j}_lifetime
78	[ -z "$rdnsstime" ] && return 1
79	ltime=$(($rdnsstime - $offset))
80	if [ "$ltime" -gt 0 ]; then
81		eval rdnss=\$nd${i}_rdnss${j}_servers
82		[ -n "$rdnss" ] && new_rdnss="$new_rdnss${new_rdnss:+ }$rdnss"
83	fi
84
85	eval dnssltime=\$nd${i}_dnssl${j}_lifetime
86	[ -z "$dnssltime" ] && return 1
87	ltime=$(($dnssltime - $offset))
88	if [ "$ltime" -gt 0 ]; then
89		eval dnssl=\$nd${i}_dnssl${j}_search
90		[ -n "$dnssl" ] && new_dnssl="$new_dnssl${new_dnssl:+ }$dnssl"
91	fi
92
93	j=$(($j + 1))
94	return 0
95}
96
97add_resolv_conf()
98{
99	conf="$signature$NL"
100	warn=true
101
102	# Loop to extract the ND DNS options using our indexed shell values
103	i=1
104	j=1
105	while true; do
106		eval acquired=\$nd${i}_acquired
107		[ -z "$acquired" ] && break
108		eval now=\$nd${i}_now
109		[ -z "$now" ] && break
110		offset=$(($now - $acquired))
111		while true; do
112			eval_nd_dns || break
113		done
114		i=$(($i + 1))
115		j=1
116	done
117	[ -n "$new_rdnss" ] && \
118	    new_domain_name_servers="$new_domain_name_servers${new_domain_name_servers:+ }$new_rdnss"
119	[ -n "$new_dnssl" ] && \
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" ] &&
135	   [ -z "$new_domain_name" ] &&
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		new_domain_search=$(uniqify $new_domain_search)
156		if valid_domainname_list $new_domain_search; then
157			conf="${conf}search $new_domain_search$NL"
158		elif ! $warn; then
159			syslog err "Invalid domain name in list:" \
160			    "$new_domain_search"
161		fi
162	fi
163	new_domain_name_servers=$(uniqify $new_domain_name_servers)
164	for x in ${new_domain_name_servers}; do
165		conf="${conf}nameserver $x$NL"
166	done
167	if type "$resolvconf" >/dev/null 2>&1; then
168		[ -n "$ifmetric" ] && export IF_METRIC="$ifmetric"
169		printf %s "$conf" | "$resolvconf" -a "$ifname"
170		return $?
171	fi
172
173	if [ -e "$resolv_conf_dir/$ifname" ]; then
174		rm -f "$resolv_conf_dir/$ifname"
175	fi
176	[ -d "$resolv_conf_dir" ] || mkdir -p "$resolv_conf_dir"
177	printf %s "$conf" > "$resolv_conf_dir/$ifname"
178	build_resolv_conf
179}
180
181remove_resolv_conf()
182{
183	if type "$resolvconf" >/dev/null 2>&1; then
184		"$resolvconf" -d "$ifname" -f
185	else
186		if [ -e "$resolv_conf_dir/$ifname" ]; then
187			rm -f "$resolv_conf_dir/$ifname"
188		fi
189		build_resolv_conf
190	fi
191}
192
193# For ease of use, map DHCP6 names onto our DHCP4 names
194case "$reason" in
195BOUND6|RENEW6|REBIND6|REBOOT6|INFORM6)
196	new_domain_name_servers="$new_dhcp6_name_servers"
197	new_domain_search="$new_dhcp6_domain_search"
198	;;
199esac
200
201if $if_configured; then
202	if $if_up || [ "$reason" = ROUTERADVERT ]; then
203		add_resolv_conf
204	elif $if_down; then
205		remove_resolv_conf
206	fi
207fi
208