zoneconf.in: Set up a temporary directory when signing.
[zoneconf] / zoneconf.in
index 21cff51..541c019 100755 (executable)
@@ -198,6 +198,13 @@ proc run {what command args} {
   }
 }
 
+proc isolate {body} {
+  ## Evaluate BODY without changing the caller's variables.  Return its
+  ## result.
+
+  eval $body
+}
+
 ###--------------------------------------------------------------------------
 ### Configuration spaces.
 ###
@@ -942,12 +949,21 @@ proc host-canonify {host} {
 
 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
+    }
   }
 }
 
@@ -992,15 +1008,15 @@ define-configuration-space zone ZONECFG {
   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" \
@@ -1016,12 +1032,19 @@ define-configuration-space zone ZONECFG {
           "-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"
     }
@@ -1113,6 +1136,16 @@ proc output-file-name {view} {
   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.
@@ -1141,13 +1174,28 @@ proc compute-zone-properties {view config} {
   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
       }
     }
@@ -1259,6 +1307,9 @@ proc write-zone-stanza {view chan config} {
     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
@@ -1279,6 +1330,9 @@ proc write-zone-stanza {view chan config} {
       }
     }
   }
+  if {![string equal $zone(allow-query) nil]} {
+    puts $chan "\tallow-query {$zone(allow-query)};"
+  }
   puts $chan "};";
 }
 
@@ -1292,7 +1346,7 @@ defcmd outputs {} {
 } {
   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] }
 }
 
@@ -1302,7 +1356,7 @@ defcmd update {} {
   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
@@ -1357,10 +1411,10 @@ 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 {}
@@ -1391,8 +1445,7 @@ provided by the named USER."
     ## 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] && \
@@ -1456,7 +1509,11 @@ defcmd sign {} {
   set rc 0
 
   ## Read the configuration.
-  confspc-eval toplevel [list source $CONFFILE]
+  isolate [list confspc-eval toplevel [list source $CONFFILE]]
+
+  ## Sometimes `dnssec-signzone' tries to write temporary files to the
+  ## current directory.  Make sure this is sensible.
+  temporary-directory
 
   ## Grind through all of the zones.
   array unset seen