Browse files

Remove the recent auto-serial feature

I had added the ability to autogenerate zone serials from disk
mtimes in a standard format, if the actual serial number in the
zonefile was the magic number zero.

On further reflection, this is a mis-feature.  It *seems* like it
might be useful for some use-cases, but in practice eventually
everyone that tries to use it will migrate away from it once they
realize all the real-world operational problems it causes with a
real set of multiple production servers (think FS timestamps, and
reinstalled machines and adding new machines to the set, etc).

At the end of the day, consistent serials either need to be set
manually in upstream data (during git commits?) or need to be set
by the zonefile deployment tooling, to keep them consistent (and
yes, some registrars will complain if your serials aren't
consistent across authservers).

This is mostly a revert of db24c50 , but a few minor intervening
changes to related lines made it not a clean perfect revert.
  • Loading branch information...
blblack committed Dec 4, 2018
1 parent 57de395 commit 565b61c09a32199e73a5c8d68048148b4a8f5591
Showing with 9 additions and 70 deletions.
  1. +0 −1
  2. +3 −10 docs/gdnsd.zonefile.podin
  3. +0 −5 include/gdnsd/file.h
  4. +0 −8 libgdnsd/file.c
  5. +4 −26 src/ltree.c
  6. +0 −5 src/zscan_rfc1035.rl
  7. +1 −14 src/ztree.h
  8. +1 −1 t/001basic/etc/zones/
@@ -45,7 +45,6 @@ This is an attempt at a human-usable breakdown of all the human-affecting change
* `$INCLUDE` files supported (use subdirectories, which are otherwise ignored, to avoid confusing them for zones)
* Symlinks now work for aliasing zones, assuming there are no explicit references to the zone name within the data. To help with that:
* `@Z` and `@F` macros implemented, which represent the original (line zero) `$ORIGIN` of the zone or the current file. You can use these in situations like: `$ORIGIN foo.@F [... records ...] $ORIGIN bar.@F`, which would otherwise be impossible without hardcoding the zone name in the second origin statement, breaking symlink zone aliasing
* If an SOA's serial is zero, it will be autogenerated from file mtimes as YYMMDDHHMM, using the timestamp of the most-recent of all included files
### gdnsdctl
@@ -9,7 +9,7 @@
$TTL 86400
@ SOA ns1 hostmaster (
0 ; serial (0 -> auto from mtime)
1 ; serial
7200 ; refresh
30M ; retry
3D ; expire
@@ -164,15 +164,8 @@ C<SOA> records' negative-caching TTL is set to the minimum of the traditional
actual TTL of the SOA record any time it is emitted, whether for negative or
positive answers.
If the serial field of an C<SOA> record is set to zero, an automatic serial
number based on the zonefile's last modification timestamp will be generated
using the format C<YYMMDDHHMM> in the UTC timezone. This gives 1-minute
resolution and will fail in the year 2043, by which time we'll have to come up
with something new. If there were C<$INCLUDE> files involved, the effective
last modification will be the latest of all recursively included files.
Additionally, gdnsd supports two special-case, non-standard virtual resource
record types DYNA and DYNC:
Additionally, gdnsd supports two special-case, non-standard
virtual resource record types DYNA and DYNC:
=head2 DYNA
@@ -24,7 +24,6 @@
#include <sys/types.h>
#include <stdbool.h>
#include <time.h>
struct gdnsd_fmap_s_;
typedef struct gdnsd_fmap_s_ gdnsd_fmap_t;
@@ -47,10 +46,6 @@ size_t gdnsd_fmap_get_len(const gdnsd_fmap_t* fmap);
const void* gdnsd_fmap_get_buf(const gdnsd_fmap_t* fmap);
// Get the mtime of the file when first mapped (1s resolution)
time_t gdnsd_fmap_get_mtime(const gdnsd_fmap_t* fmap);
// Destructs the fmap_t object, which includes unmap() of the memory
// returned via fmap_get_buf().
// If a destruction step fails, this returns true (in which case the file data
@@ -31,12 +31,10 @@
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <time.h>
struct gdnsd_fmap_s_ {
void* buf;
size_t len;
time_t mtime;
gdnsd_fmap_t* gdnsd_fmap_new(const char* fn, const bool seq)
@@ -92,7 +90,6 @@ gdnsd_fmap_t* gdnsd_fmap_new(const char* fn, const bool seq)
gdnsd_fmap_t* fmap = xmalloc(sizeof(*fmap));
fmap->buf = mapbuf;
fmap->len = len;
fmap->mtime = st.st_mtime;
return fmap;
@@ -109,11 +106,6 @@ size_t gdnsd_fmap_get_len(const gdnsd_fmap_t* fmap)
return fmap->len;
time_t gdnsd_fmap_get_mtime(const gdnsd_fmap_t* fmap)
return fmap->mtime;
bool gdnsd_fmap_delete(gdnsd_fmap_t* fmap)
@@ -35,7 +35,6 @@
#include <fcntl.h>
#include <unistd.h>
#include <limits.h>
#include <time.h>
// special label used to hide out-of-zone glue
// inside zone root node child lists
@@ -1199,20 +1198,6 @@ static bool ltree_postproc(const zone_t* zone, bool (*fn)(const uint8_t**, const
return _ltree_proc_inner(fn, lstack, zone->root, zone, 0, false);
static unsigned make_serial(time_t mtime)
unsigned rv = 0;
struct tm mt;
if (gmtime_r(&mtime, &mt))
rv = ((((unsigned)mt.tm_year) % 100U) * 100000000U)
+ ((((unsigned)mt.tm_mon) + 1U) * 1000000U)
+ (((unsigned)mt.tm_mday) * 10000U)
+ (((unsigned)mt.tm_hour) * 100U)
+ (unsigned)mt.tm_min;
return rv;
static bool ltree_postproc_zroot_phase1(zone_t* zone)
@@ -1255,15 +1240,8 @@ static bool ltree_postproc_zroot_phase1(zone_t* zone)
if (!ok)
log_zwarn("Zone '%s': SOA Master does not match any NS records for this zone", logf_dname(zone->dname));
if (zroot_soa->times[0]) {
// Parser set explicit SOA serial value, use that to set zone-level one
zone->serial = ntohl(zroot_soa->times[0]);
} else if (zone->mtime) {
// SOA serial is zero, but mtime was set, so use automagic value for both serials
zone->serial = make_serial(zone->mtime);
zroot_soa->times[0] = htonl(zone->serial);
// copy SOA Serial field up to zone_t for easy comparisons
zone->serial = ntohl(zroot_soa->times[0]);
return false;
@@ -1323,8 +1301,8 @@ bool ltree_postproc_zone(zone_t* zone)
// zroot phase1 is a readonly check of zone basics (e.g. NS/SOA existence),
// also does SOA serial magic between zone_t and the actual SOA
// zroot phase1 is a readonly check of zone basics
// (e.g. NS/SOA existence), also sets zone->serial
if (unlikely(ltree_postproc_zroot_phase1(zone)))
return true;
@@ -34,7 +34,6 @@
#include <unistd.h>
#include <setjmp.h>
#include <errno.h>
#include <time.h>
@@ -294,10 +293,6 @@ static bool zscan_do(zone_t* zone, const uint8_t* origin, const char* fn, const
return failed;
const time_t file_mtime = gdnsd_fmap_get_mtime(fmap);
if (file_mtime > zone->mtime)
zone->mtime = file_mtime;
const size_t bufsize = gdnsd_fmap_get_len(fmap);
const char* buf = gdnsd_fmap_get_buf(fmap);
@@ -31,23 +31,10 @@ typedef struct _zone_struct zone_t;
#include <stdbool.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
// re: zone_t mtime/serial fields:
// These initialize to zero during zone_new(), and the zone data loader can
// optionally set them at any time between zone_new() and zone_finalize().
// At the time of zone_finalize(), the following logic is applied:
// 1) If the zone's SOA record has a non-zero serial value, the mtime is
// ignored and the explicit SOA serial is copied to the zone-level serial.
// 2) If the zone's SOA record has a zero value and the mtime field has a nonzero value,
// an automatic serial is calculated from the mtime and copied to both the
// zone-level serial and the SOA record serial.
// 3) If both the SOA record and the serial field here are zero, they're left at zero.
// 4) The zone-level serial is what's output in log messages about zone loading.
struct _zone_struct {
unsigned hash; // hash of dname
time_t mtime; // effective mtime of the zone data
unsigned serial; // SOA serial
unsigned serial; // SOA serial from zone data
char* src; // string description of src, e.g. ""
const uint8_t* dname; // zone name as a dname (stored in ->arena)
ltarena_t* arena; // arena for dname/label storage
@@ -1,5 +1,5 @@
@ SOA ns1 hostmaster (
0 ; serial (0 -> auto from mtime)
1 ; serial
7200 ; refresh
1800 ; retry
259200 ; expire

0 comments on commit 565b61c

Please sign in to comment.