}
}
+proc isolate {body} {
+ ## Evaluate BODY without changing the caller's variables. Return its
+ ## result.
+
+ eval $body
+}
+
###--------------------------------------------------------------------------
### Configuration spaces.
###
proc local-address-p {addr} {
## Answer whether the ADDR is one of the host's addresses.
+ global env
- if {[catch { set sk [socket -server {} -myaddr $addr 0] }]} {
- return false
+ if {[info exists env(ZONECONF_LOCAL_ADDRESSES)]} {
+ if {[string match "*:$addr:*" ":$env(ZONECONF_LOCAL_ADDRESSES):"]} {
+ return true
+ } else {
+ return false
+ }
} else {
- close $sk
- return true
+ if {[catch { set sk [socket -server {} -myaddr $addr 0] }]} {
+ return false
+ } else {
+ close $sk
+ return true
+ }
}
}
define-configuration-space dynamic ZONECFG {
prefix "ddns-"
define-simple key "ddns"
+ define-simple auto-dnssec off
define-list types {A TXT PTR}
define policy {body} {
define-simple dir-mode 2775
define-simple zone-file "%v/%z.zone"
define-simple soa-format increment
+ define-simple allow-query nil
define-list views *
define-list sign-views {}
define-list signzone-command \
[list "$BINDPROGS/dnssec-signzone" \
- "-g" \
"-S" \
"-K%h/key" \
"-d%h/ds" \
- "-s-3600" "-e+176400" \
+ "-s-3600" "-e+176400" "-i90000" \
"-N%q" \
"-o%z" \
"-f%o" \
"%f"]
- define-simple auto-dnssec off
define-list reload-command [list "$BINDPROGS/rndc" "reload" "%z" "IN" "%v"]
define-list autosign-command [list "$BINDPROGS/rndc" "sign" "%z" "IN" "%v"]
define-list checkzone-command \
"-Sfail" \
"-Wfail" \
"%z" "%f"]
+ define-list also-notify nil
define setvar {name value} {
dict set ZONECFG(var) $name $value
}
define primary {map} {
+ ## There's a grim hack here: a primary-address entry may have the form
+ ## REAL!FAKE. If the REAL address is not a local address then this
+ ## is used as the master address; otherwise the FAKE address is used.
+ ## This is useful for inter-view updates of dynamic zones on the same
+ ## host. I suggest abusing 127.0.0.0/8 addresses for this kind of
+ ## chicanery.
if {[llength $map] % 2} {
error "master map must have an even number of items"
}
return [string map [list %v $view] $ZONECFG(conf-file)]
}
+proc temporary-directory {} {
+ ## Create a temporary directory and set as the working directory.
+
+ global ZONECFG
+ set tmp [file join $ZONECFG(home-dir) "tmp"]
+ file mkdir $tmp
+ cd $tmp
+ return $tmp
+}
+
proc compute-zone-properties {view config} {
## Derive interesting information from the zone configuration plist CONFIG,
## relative to the stated server VIEW. Return a new plist.
if {[info exists zone(mapped-view)]} {
foreach {outview hosts} $zone(master-map) {
if {[string match $outview $zone(mapped-view)]} {
- set zone(masters) $hosts
+ set masters {}
set zone(config-type) slave
foreach host $hosts {
- if {[local-address-p $host]} {
+ set bang [string first "!" $host]
+ if {$bang >= 0} {
+ set after [string range $host [expr {$bang + 1}] end]
+ if {$bang} {
+ set before [string range $host 0 [expr {$bang - 1}]]
+ } else {
+ set before $after
+ }
+ if {[local-address-p $before]} {
+ set host $after
+ } else {
+ set host $before
+ }
+ } elseif {[local-address-p $host]} {
set zone(config-type) master
}
+ lappend masters $host
}
+ set zone(masters) $masters
break
}
}
array set zone $info
set outfile "$zone(server-file-name).new"
- if {![run "zone `$zone(name)' in view `$zone(mapped-view)'" \
+ if {![run "sign zone `$zone(name)' in view `$zone(mapped-view)'" \
$zone(signzone-command) \
"%h" $zone(home-dir) \
"%m" $zone(static-dir) \
master {
puts $chan "\ttype master;"
puts $chan "\tfile \"$zone(server-file-name)\";"
+ if {![string equal $zone(also-notify) "nil"]} {
+ puts $chan "\talso-notify { [join $zone(also-notify) {; }]; };"
+ }
switch -exact -- $zone(type) {
- dynamic { write-ddns-update-policy "\t" $chan $config }
+ dynamic {
+ write-ddns-update-policy "\t" $chan $config
+ if {![string equal $zone(ddns-auto-dnssec) off]} {
+ puts $chan "\tauto-dnssec $zone(ddns-auto-dnssec);"
+ }
+ }
}
}
slave {
foreach host $zone(masters) { lappend masters [host-addr $host] }
puts $chan "\tmasters { [join $masters {; }]; };"
puts $chan "\tfile \"$zone(file-name)\";"
- if {![string equal $zone(auto-dnssec) off]} {
- puts $chan "\tauto-dnssec $zone(auto-dnssec);"
- }
switch -exact -- $zone(type) {
dynamic { puts $chan "\tallow-update-forwarding { any; };" }
}
}
}
+ if {![string equal $zone(allow-query) nil]} {
+ puts $chan "\tallow-query {$zone(allow-query)};"
+ }
puts $chan "};";
}
} {
global ZONECFG CONFFILE
- confspc-eval toplevel [list source $CONFFILE]
+ isolate [list confspc-eval toplevel [list source $CONFFILE]]
foreach view $ZONECFG(all-views) { puts [output-file-name $view] }
}
global ZONECFG ZONES CONFFILE
## Read the configuration.
- confspc-eval toplevel [list source $CONFFILE]
+ isolate [list confspc-eval toplevel [list source $CONFFILE]]
## Safely update the files.
set win false
defcmd install {user view name} {
help-text "Install a new zone file.
- The file is for the given zone NAME and \(user-side) VIEW. The file is
- provided by the named USER"
+The file is for the given zone NAME and \(user-side) VIEW. The file is
+provided by the named USER."
} {
global QUIS ZONECFG ZONES CONFFILE errorInfo errorCode
## Read the configuration.
- confspc-eval toplevel [list source $CONFFILE]
+ isolate [list confspc-eval toplevel [list source $CONFFILE]]
## Make sure there's a temporary directory.
- file mkdir [file join $ZONECFG(home-dir) "tmp"]
+ set tmpdir [temporary-directory]
## Keep track of cleanup jobs.
set cleanup {}
## Make a new temporary file to read the zone into.
set pid [pid]
for {set i 0} {$i < 1000} {incr i} {
- set tmp [file join $ZONECFG(home-dir) "tmp" \
- "tmp.$pid.$i.$user.$name"]
+ set tmp [file join $tmpdir "tmp.$pid.$i.$user.$name"]
if {![catch { set chan [open $tmp {WRONLY CREAT EXCL}] } msg]} {
break
} elseif {[string equal [lindex $errorCode 0] POSIX] && \
}
## If the zone wants signing, better to do that now.
- if {![sign-zone-file $matchinfo keep $tmp]} {
+ if {$zone(sign) && ![sign-zone-file $matchinfo keep $tmp]} {
eval $cleanup
exit 2
}
set rc 0
## Read the configuration.
- confspc-eval toplevel [list source $CONFFILE]
+ isolate [list confspc-eval toplevel [list source $CONFFILE]]
## Grind through all of the zones.
array unset seen
}
}
} elseif {[string equal $zone(type) dynamic] &&
- ![string equal $zone(auto-dnssec) off]} {
+ ![string equal $zone(ddns-auto-dnssec) off]} {
## Dynamic zone: get BIND to re-sign it.
if {![run "re-sign zone `$zone(name) in server view `$view'" \