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 9 resolv_conf_dir="$state_dir/resolv.conf" 10 NL=" 11 " 12 : ${resolvconf:=resolvconf} 13 14 build_resolv_conf() 15 { 16 local cf="$state_dir/resolv.conf.$ifname" 17 local interfaces= header= search= srvs= servers= x= 18 19 # Build a list of interfaces 20 interfaces=$(list_interfaces "$resolv_conf_dir") 21 22 # Build the resolv.conf 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 # For now, we ignore the lifetime of the DNS options unless they 74 # are absent or zero. 75 # In this case they are removed from consideration. 76 # See draft-gont-6man-slaac-dns-config-issues-01 for issues 77 # regarding DNS option lifetime in ND messages. 78 eval_nd_dns() 79 { 80 81 eval ltime=\$nd${i}_rdnss${j}_lifetime 82 if [ -z "$ltime" -o "$ltime" = 0 ]; then 83 rdnss= 84 else 85 eval rdnss=\$nd${i}_rdnss${j}_servers 86 fi 87 eval ltime=\$nd${i}_dnssl${j}_lifetime 88 if [ -z "$ltime" -o "$ltime" = 0 ]; then 89 dnssl= 90 else 91 eval dnssl=\$nd${i}_dnssl${j}_search 92 fi 93 94 [ -z "$rdnss" -a -z "$dnssl" ] && return 1 95 96 [ -n "$rdnss" ] && new_rdnss="$new_rdnss${new_rdnss:+ }$rdnss" 97 [ -n "$dnssl" ] && new_dnssl="$new_dnssl${new_dnssl:+ }$dnssl" 98 j=$(($j + 1)) 99 return 0 100 } 101 102 add_resolv_conf() 103 { 104 local x= conf="$signature$NL" warn=true 105 local i j ltime rdnss dnssl new_rdnss new_dnssl 106 107 # Loop to extract the ND DNS options using our indexed shell values 108 i=1 109 j=1 110 while true; do 111 while true; do 112 eval_nd_dns || break 113 done 114 i=$(($i + 1)) 115 j=1 116 eval_nd_dns || break 117 done 118 [ -n "$new_rdnss" ] && \ 119 new_domain_name_servers="$new_domain_name_servers${new_domain_name_servers:+ }$new_rdnss" 120 [ -n "$new_dnssl" ] && \ 121 new_domain_search="$new_domain_search${new_domain_search:+ }$new_dnssl" 122 123 # Derive a new domain from our various hostname options 124 if [ -z "$new_domain_name" ]; then 125 if [ "$new_dhcp6_fqdn" != "${new_dhcp6_fqdn#*.}" ]; then 126 new_domain_name="${new_dhcp6_fqdn#*.}" 127 elif [ "$new_fqdn" != "${new_fqdn#*.}" ]; then 128 new_domain_name="${new_fqdn#*.}" 129 elif [ "$new_host_name" != "${new_host_name#*.}" ]; then 130 new_domain_name="${new_host_name#*.}" 131 fi 132 fi 133 134 # If we don't have any configuration, remove it 135 if [ -z "$new_domain_name_servers" -a \ 136 -z "$new_domain_name" -a \ 137 -z "$new_domain_search" ]; then 138 remove_resolv_conf 139 return $? 140 fi 141 142 if [ -n "$new_domain_name" ]; then 143 set -- $new_domain_name 144 if valid_domainname "$1"; then 145 conf="${conf}domain $1$NL" 146 else 147 syslog err "Invalid domain name: $1" 148 fi 149 # If there is no search this, make this one 150 if [ -z "$new_domain_search" ]; then 151 new_domain_search="$new_domain_name" 152 [ "$new_domain_name" = "$1" ] && warn=true 153 fi 154 fi 155 if [ -n "$new_domain_search" ]; then 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 for x in ${new_domain_name_servers}; do 164 conf="${conf}nameserver $x$NL" 165 done 166 if type "$resolvconf" >/dev/null 2>&1; then 167 [ -n "$ifmetric" ] && export IF_METRIC="$ifmetric" 168 printf %s "$conf" | "$resolvconf" -a "$ifname" 169 return $? 170 fi 171 172 if [ -e "$resolv_conf_dir/$ifname" ]; then 173 rm -f "$resolv_conf_dir/$ifname" 174 fi 175 [ -d "$resolv_conf_dir" ] || mkdir -p "$resolv_conf_dir" 176 printf %s "$conf" > "$resolv_conf_dir/$ifname" 177 build_resolv_conf 178 } 179 180 remove_resolv_conf() 181 { 182 if type "$resolvconf" >/dev/null 2>&1; then 183 "$resolvconf" -d "$ifname" -f 184 else 185 if [ -e "$resolv_conf_dir/$ifname" ]; then 186 rm -f "$resolv_conf_dir/$ifname" 187 fi 188 build_resolv_conf 189 fi 190 } 191 192 # For ease of use, map DHCP6 names onto our DHCP4 names 193 case "$reason" in 194 BOUND6|RENEW6|REBIND6|REBOOT6|INFORM6) 195 new_domain_name_servers="$new_dhcp6_name_servers" 196 new_domain_search="$new_dhcp6_domain_search" 197 ;; 198 esac 199 200 if $if_up || [ "$reason" = ROUTERADVERT ]; then 201 add_resolv_conf 202 elif $if_down; then 203 remove_resolv_conf 204 fi 205