/contrib/openresolv/resolvconf.in

https://bitbucket.org/freebsd/freebsd-head/ · Autoconf · 421 lines · 348 code · 27 blank · 46 comment · 77 complexity · 0e07b53d3caff326ada990d2d6a583cd MD5 · raw file

  1. #!/bin/sh
  2. # Copyright (c) 2007-2009 Roy Marples
  3. # All rights reserved
  4. # Redistribution and use in source and binary forms, with or without
  5. # modification, are permitted provided that the following conditions
  6. # are met:
  7. # * Redistributions of source code must retain the above copyright
  8. # notice, this list of conditions and the following disclaimer.
  9. # * Redistributions in binary form must reproduce the above
  10. # copyright notice, this list of conditions and the following
  11. # disclaimer in the documentation and/or other materials provided
  12. # with the distribution.
  13. #
  14. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  15. # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  16. # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  17. # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  18. # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  19. # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  20. # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  21. # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  22. # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  23. # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  24. # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  25. RESOLVCONF="$0"
  26. SYSCONFDIR=@SYSCONFDIR@
  27. LIBEXECDIR=@LIBEXECDIR@
  28. VARDIR=@VARDIR@
  29. # Support original resolvconf configuration layout
  30. # as well as the openresolv config file
  31. if [ -f "$SYSCONFDIR"/resolvconf.conf ]; then
  32. . "$SYSCONFDIR"/resolvconf.conf
  33. [ -n "$state_dir" ] && VARDIR="$state_dir"
  34. elif [ -d "$SYSCONFDIR/resolvconf" ]; then
  35. SYSCONFDIR="$SYSCONFDIR/resolvconf"
  36. if [ -f "$SYSCONFDIR"/interface-order ]; then
  37. interface_order="$(cat "$SYSCONFDIR"/interface-order)"
  38. fi
  39. fi
  40. IFACEDIR="$VARDIR/interfaces"
  41. METRICDIR="$VARDIR/metrics"
  42. PRIVATEDIR="$VARDIR/private"
  43. : ${dynamic_order:=tap[0-9]* tun[0-9]* vpn vpn[0-9]* ppp[0-9]* ippp[0-9]*}
  44. : ${interface_order:=lo lo[0-9]*}
  45. error_exit()
  46. {
  47. echo "$*" >&2
  48. exit 1
  49. }
  50. usage()
  51. {
  52. cat <<-EOF
  53. Usage: ${RESOLVCONF##*/} [options]
  54. Inform the system about any DNS updates.
  55. Options:
  56. -a \$INTERFACE Add DNS information to the specified interface
  57. (DNS supplied via stdin in resolv.conf format)
  58. -m metric Give the added DNS information a metric
  59. -p Mark the interface as private
  60. -d \$INTERFACE Delete DNS information from the specified interface
  61. -f Ignore non existant interfaces
  62. -I Init the state dir
  63. -u Run updates from our current DNS information
  64. -l [\$PATTERN] Show DNS information, optionally from interfaces
  65. that match the specified pattern
  66. -i [\$PATTERN] Show interfaces that have supplied DNS information
  67. optionally from interfaces that match the specified
  68. pattern
  69. -v [\$PATTERN] echo NEWDOMAIN, NEWSEARCH and NEWNS variables to
  70. the console
  71. -h Show this help cruft
  72. EOF
  73. [ -z "$1" ] && exit 0
  74. echo
  75. error_exit "$*"
  76. }
  77. echo_resolv()
  78. {
  79. local line=
  80. [ -n "$1" -a -e "$IFACEDIR/$1" ] || return 1
  81. echo "# resolv.conf from $1"
  82. # Our variable maker works of the fact each resolv.conf per interface
  83. # is separated by blank lines.
  84. # So we remove them when echoing them.
  85. while read line; do
  86. [ -n "$line" ] && echo "$line"
  87. done < "$IFACEDIR/$1"
  88. echo
  89. }
  90. # Parse resolv.conf's and make variables
  91. # for domain name servers, search name servers and global nameservers
  92. parse_resolv()
  93. {
  94. local line= ns= ds= search= d= n= newns=
  95. local new=true iface= private=false p=
  96. echo "DOMAINS="
  97. echo "SEARCH=\"$search_domains\""
  98. # let our subscribers know about global nameservers
  99. for n in $name_servers; do
  100. case "$n" in
  101. 127.*|0.0.0.0|255.255.255.255|::1) :;;
  102. *) newns="$newns${newns:+ }$n";;
  103. esac
  104. done
  105. echo "NAMESERVERS=\"$newns\""
  106. echo "LOCALNAMESERVERS="
  107. newns=
  108. while read line; do
  109. case "$line" in
  110. "# resolv.conf from "*)
  111. if ${new}; then
  112. iface="${line#\# resolv.conf from *}"
  113. new=false
  114. if [ -e "$PRIVATEDIR/$iface" ]; then
  115. private=true
  116. else
  117. # Allow expansion
  118. cd "$IFACEDIR"
  119. private=false
  120. for p in $private_interfaces; do
  121. if [ "$p" = "$iface" ]; then
  122. private=true
  123. break
  124. fi
  125. done
  126. fi
  127. fi
  128. ;;
  129. "nameserver "*)
  130. case "${line#* }" in
  131. 127.*|0.0.0.0|255.255.255.255|::1)
  132. echo "LOCALNAMESERVERS=\"\$LOCALNAMESERVERS ${line#* }\""
  133. continue
  134. ;;
  135. esac
  136. ns="$ns${line#* } "
  137. ;;
  138. "domain "*|"search "*)
  139. search="${line#* }"
  140. ;;
  141. *)
  142. [ -n "$line" ] && continue
  143. if [ -n "$ns" -a -n "$search" ]; then
  144. newns=
  145. for n in $ns; do
  146. newns="$newns${newns:+,}$n"
  147. done
  148. ds=
  149. for d in $search; do
  150. ds="$ds${ds:+ }$d:$newns"
  151. done
  152. echo "DOMAINS=\"\$DOMAINS $ds\""
  153. fi
  154. echo "SEARCH=\"\$SEARCH $search\""
  155. if ! $private; then
  156. echo "NAMESERVERS=\"\$NAMESERVERS $ns\""
  157. fi
  158. ns=
  159. search=
  160. new=true
  161. ;;
  162. esac
  163. done
  164. }
  165. uniqify()
  166. {
  167. local result=
  168. while [ -n "$1" ]; do
  169. case " $result " in
  170. *" $1 "*);;
  171. *) result="$result $1";;
  172. esac
  173. shift
  174. done
  175. echo "${result# *}"
  176. }
  177. list_resolv()
  178. {
  179. [ -d "$IFACEDIR" ] || return 0
  180. local report=false list= retval=0 cmd="$1"
  181. shift
  182. # If we have an interface ordering list, then use that.
  183. # It works by just using pathname expansion in the interface directory.
  184. if [ -n "$1" ]; then
  185. list="$@"
  186. $force || report=true
  187. else
  188. cd "$IFACEDIR"
  189. for i in $interface_order; do
  190. [ -e "$i" ] && list="$list $i"
  191. done
  192. for i in $dynamic_order; do
  193. if [ -e "$i" -a ! -e "$METRICDIR/"*" $i" ]; then
  194. list="$list $i"
  195. fi
  196. done
  197. if [ -d "$METRICDIR" ]; then
  198. cd "$METRICDIR"
  199. for i in *; do
  200. list="$list ${i#* }"
  201. done
  202. fi
  203. list="$list *"
  204. fi
  205. cd "$IFACEDIR"
  206. for i in $(uniqify $list); do
  207. # Only list interfaces which we really have
  208. if ! [ -e "$i" ]; then
  209. if $report; then
  210. echo "No resolv.conf for interface $i" >&2
  211. retval=$(($retval + 1))
  212. fi
  213. continue
  214. fi
  215. if [ "$cmd" = i -o "$cmd" = "-i" ]; then
  216. printf "$i "
  217. else
  218. echo_resolv "$i"
  219. fi
  220. done
  221. [ "$cmd" = i -o "$cmd" = "-i" ] && echo
  222. return $retval
  223. }
  224. make_vars()
  225. {
  226. eval "$(list_resolv -l "$@" | parse_resolv)"
  227. # Ensure that we only list each domain once
  228. newdomains=
  229. for d in $DOMAINS; do
  230. dn="${d%%:*}"
  231. case " $newdomains" in
  232. *" ${dn}:"*) continue;;
  233. esac
  234. newdomains="$newdomains${newdomains:+ }$dn:"
  235. newns=
  236. for nd in $DOMAINS; do
  237. if [ "$dn" = "${nd%%:*}" ]; then
  238. ns="${nd#*:}"
  239. while [ -n "$ns" ]; do
  240. case ",$newns," in
  241. *,${ns%%,*},*) ;;
  242. *) newns="$newns${newns:+,}${ns%%,*}";;
  243. esac
  244. [ "$ns" = "${ns#*,}" ] && break
  245. ns="${ns#*,}"
  246. done
  247. fi
  248. done
  249. newdomains="$newdomains$newns"
  250. done
  251. echo "DOMAINS='$newdomains'"
  252. echo "SEARCH='$(uniqify $SEARCH)'"
  253. echo "NAMESERVERS='$(uniqify $NAMESERVERS)'"
  254. echo "LOCALNAMESERVERS='$(uniqify $LOCALNAMESERVERS)'"
  255. }
  256. force=false
  257. while getopts a:d:fhIilm:puv OPT; do
  258. case "$OPT" in
  259. f) force=true;;
  260. h) usage;;
  261. m) IF_METRIC="$OPTARG";;
  262. p) IF_PRIVATE=1;;
  263. '?') ;;
  264. *) cmd="$OPT"; iface="$OPTARG";;
  265. esac
  266. done
  267. shift $(($OPTIND - 1))
  268. args="$iface${iface:+ }$@"
  269. # -I inits the state dir
  270. if [ "$cmd" = I ]; then
  271. if [ -d "$VARDIR" ]; then
  272. rm -rf "$VARDIR"/*
  273. fi
  274. exit $?
  275. fi
  276. # -l lists our resolv files, optionally for a specific interface
  277. if [ "$cmd" = l -o "$cmd" = i ]; then
  278. list_resolv "$cmd" "$args"
  279. exit $?
  280. fi
  281. # Not normally needed, but subscribers should be able to run independently
  282. if [ "$cmd" = v ]; then
  283. make_vars "$iface"
  284. exit $?
  285. fi
  286. # Test that we have valid options
  287. if [ "$cmd" = a -o "$cmd" = d ]; then
  288. if [ -z "$iface" ]; then
  289. usage "Interface not specified"
  290. fi
  291. elif [ "$cmd" != u ]; then
  292. [ -n "$cmd" -a "$cmd" != h ] && usage "Unknown option $cmd"
  293. usage
  294. fi
  295. if [ "$cmd" = a ]; then
  296. for x in '/' \\ ' ' '*'; do
  297. case "$iface" in
  298. *[$x]*) error_exit "$x not allowed in interface name";;
  299. esac
  300. done
  301. for x in '.' '-' '~'; do
  302. case "$iface" in
  303. [$x]*) error_exit \
  304. "$x not allowed at start of interface name";;
  305. esac
  306. done
  307. [ "$cmd" = a -a -t 0 ] && error_exit "No file given via stdin"
  308. fi
  309. if [ ! -d "$IFACEDIR" ]; then
  310. if [ ! -d "$VARDIR" ]; then
  311. if [ -L "$VARDIR" ]; then
  312. dir="$(readlink "$VARDIR")"
  313. # link maybe relative
  314. cd "${VARDIR%/*}"
  315. if ! mkdir -m 0755 -p "$dir"; then
  316. error_exit "Failed to create needed" \
  317. "directory $dir"
  318. fi
  319. else
  320. if ! mkdir -m 0755 -p "$VARDIR"; then
  321. error_exit "Failed to create needed" \
  322. "directory $VARDIR"
  323. fi
  324. fi
  325. fi
  326. mkdir -m 0755 -p "$IFACEDIR" || \
  327. error_exit "Failed to create needed directory $IFACEDIR"
  328. else
  329. # Delete any existing information about the interface
  330. if [ "$cmd" = d ]; then
  331. cd "$IFACEDIR"
  332. for i in $args; do
  333. if [ "$cmd" = d -a ! -e "$i" ]; then
  334. $force && continue
  335. error_exit "No resolv.conf for" \
  336. "interface $i"
  337. fi
  338. rm -f "$i" "$METRICDIR/"*" $i" \
  339. "$PRIVATEDIR/$i" || exit $?
  340. done
  341. fi
  342. fi
  343. if [ "$cmd" = a ]; then
  344. # Read resolv.conf from stdin
  345. resolv="$(cat)"
  346. # If what we are given matches what we have, then do nothing
  347. if [ -e "$IFACEDIR/$iface" ]; then
  348. if [ "$(echo "$resolv")" = \
  349. "$(cat "$IFACEDIR/$iface")" ]
  350. then
  351. exit 0
  352. fi
  353. rm "$IFACEDIR/$iface"
  354. fi
  355. echo "$resolv" >"$IFACEDIR/$iface" || exit $?
  356. [ ! -d "$METRICDIR" ] && mkdir "$METRICDIR"
  357. rm -f "$METRICDIR/"*" $iface"
  358. if [ -n "$IF_METRIC" ]; then
  359. # Pad metric to 6 characters, so 5 is less than 10
  360. while [ ${#IF_METRIC} -le 6 ]; do
  361. IF_METRIC="0$IF_METRIC"
  362. done
  363. echo " " >"$METRICDIR/$IF_METRIC $iface"
  364. fi
  365. case "$IF_PRIVATE" in
  366. [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1)
  367. if [ ! -d "$PRIVATEDIR" ]; then
  368. [ -e "$PRIVATEDIR" ] && rm "$PRIVATEDIR"
  369. mkdir "$PRIVATEDIR"
  370. fi
  371. [ -d "$PRIVATEDIR" ] && echo " " >"$PRIVATEDIR/$iface"
  372. ;;
  373. *)
  374. if [ -e "$PRIVATEDIR/$iface" ]; then
  375. rm -f "$PRIVATEDIR/$iface"
  376. fi
  377. ;;
  378. esac
  379. fi
  380. eval "$(make_vars)"
  381. export RESOLVCONF DOMAINS SEARCH NAMESERVERS LOCALNAMESERVERS
  382. : ${list_resolv:=list_resolv -l}
  383. retval=0
  384. for script in "$LIBEXECDIR"/*; do
  385. if [ -f "$script" ]; then
  386. if [ -x "$script" ]; then
  387. "$script" "$cmd" "$iface"
  388. else
  389. (. "$script" "$cmd" "$iface")
  390. fi
  391. retval=$(($retval + $?))
  392. fi
  393. done
  394. exit $retval