
=head1 NAME

gdnsd.config - gdnsd configuration file

=head1 SYNOPSIS

  options => {
    log_stats => 86400
    tcp_timeout => 15 ; zonefile-style comment
    include_optional_ns => true
    listen => [ 127.0.0.1, 192.0.2.1 ]
  }

  # shell-style comment

  service_types => {
    foosvc => { vhost => www.example.com, url_path => "/checkme" }
    barsvc => $include{bar-svc.cfg}
    $include{other-services.cfg}
  }

  plugins => {
    null => {}
  }

=head1 DESCRIPTION

This man page describes the syntax of the primary gdnsd
configuration file.

The lower-level syntax and structure of the configuration
language is described in detail at the end of this document,
but it should be fairly intuitive from the example above.  It
is effectively a generic data structure language allowing
arbitrarily-nested ordered hashes, ordered arrays, and scalar
values.  Double-quotes are used to quote scalars containing
whitespace or various ambiguous metacharacters.

The top-level implicit hash of a gdnsd configuration file allows
only 3 legal keys: B<options>, B<service_types>, and B<plugins>.

Any of them which are present must have a Hash as their value.

All of them are optional, as is the configuration file itself.
if you're happy with an all-default configuration, you can
simply not have a config file at all.

=head1 OPTIONS HASH

These options control the overall behavior of L<gdnsd(8)>.

=over 4

=item B<username>

String, defaults to "gdnsd".  This is the username the daemon drops
privileges to the uid/gid of on startup if started as root.

=item B<zones_default_ttl>

Integer seconds, default 86400.  This is the global default
time-to-live for any record in any zonefile.  It can be overridden with
a more specific default within zone files themselves via the C<$TTL>
directive (see L<gdnsd.zonefile(5)>).

=item B<dns_port>

Integer port, 1-65535, default 53.  This is the global default port
number for DNS listener addresses which do not specify port numbers
themselves.

=item B<http_port>

Integer port, 1-65535, default 3506.  This is the default HTTP port
number for stats listener addresses which do not specify port numbers
themselves.

=item B<listen>

The listen option specifies the socket addresses the server listens on
for DNS requests.

A listen-address specification is an IP (v4 or v6) address specified as
a numeric string with standard formatting (anything numeric that
C<getaddrinfo()> supports on your platform), optionally followed by a
colon and a port number.  If no port number is specified, it defaults
to the value from C<dns_port>, which defaults to C<53>.

Due to various parsing ambiguities, if you wish to specify a
non-default port number for an IPv6 listen address, you will have to
enclose the address part in square brackets, and then enclose the
entire string in double-quotes.

The structure of the listen option as a whole can take one of three
basic forms.  In its simplest form, it is just a single listen-address
specification as a string, such as:

  options => { listen = 192.0.2.1 }

It can also take the form of an array of such addresses, as in:

  options => {
    listen = [
      192.0.2.1,
      192.0.2.2,
      2001:DB8::1,
      "[2001:DB8::1234]:5353",
    ]
  }

Finally, it can also be a hash where the keys are listen addresses, and
the values are per-address options, as in:

  options => {
    listen => {
      192.0.2.1 => {
        tcp_timeout = 7
      },
      192.0.2.2:5353 => {
        udp_threads = 5
      },
    }
  }

The per-address options (which are identical to, and locally override,
the global option of the same name) are C<tcp_threads>,
C<tcp_timeout>, C<tcp_clients_per_thread>, C<udp_threads>, C<udp_recv_width>,
C<udp_rcvbuf>, and C<udp_sndbuf>.

There are also two special singalur string values: C<any> and C<scan>.

If set to C<any>, the daemon will listen on the C<dns_port> port (default 53)
on C<0.0.0.0> and C<::> (if IPv6 support is detected).

  options => { listen => any }

If set to C<scan>, scan all available IP (v4 and v6) network interfaces
via C<getifaddrs()> and set up a separate listener on the C<dns_port>
port (again, default 53) for each address found.

  options => { listen => scan }

If the listen option isn't specified at all, for historical compatibility reasons
the current default is C<scan>.  However, this default is expected to change to
C<any> in a future version, so you should specify C<scan> explicitly if you require
this behavior.

=item B<http_listen>

Basically like B<listen> above, but used for the HTTP listener (serving
stats information), and defaulting to port 3506.  The hash form isn't
supported as there are no per-address options.  Also, the default
addresses are the IPv4 and IPv6 (if supported) any-addresses
(C<0.0.0.0> and C<::>), rather than the iterated per-interface
addresses that the DNS listener uses.

It makes common sense to restrict access to this service via firewall
rules, as the data served leaks information about the rate and nature
of your DNS traffic.  This is mostly intended for your own internal
monitoring purposes.

=item B<tcp_threads>

Integer, default 1, min 0, max 1024.  This is the number of separate
TCP listening sockets and corresponding listener threads that will be created
for each DNS listener address.  On a multi-core host, increasing this
parameter (up to at most a small multiple of the CPU core count) may
increase overall performance.  Note that on hosts without SO_REUSEPORT
support (notably Linux < 3.9, Solaris), any setting greater than 1 will
be forced to 1 with a warning, as support multiple sockets/threads
per-address are not supported without SO_REUSEPORT.

=item B<udp_threads>

Exactly like C<tcp_threads>, but for UDP sockets per DNS listening address.

=item B<tcp_clients_per_thread>

Integer, default 128, min 1, max 65535.  This is maximum number of tcp
DNS connections gdnsd will allow to occur in parallel per listening tcp
socket.  Once this limit is reached by a given socket, no new
connections will be allowed to that socket until one of the existing
ones closes or times out.  Note that sockets map 1:1 to threads, and thus
the total client limit for connecting to a given address would be
C<tcp_clients_per_thread * tcp_threads> for a given address.

=item B<tcp_timeout>

Integer seconds, default 5, min 3, max 60.  TCP DNS connections will be
forcibly shut down if they go idle without receiving and responding to
a valid query for this many seconds.  L<gdnsd(8)> allows multiple
requests per connection, and this idle timeout applies to the time
between requests as well.

=item B<udp_recv_width>

Integer, default 8, min 1, max 64.  On supported Linux kernels this
setting tunes the use of more efficient interfaces to receive and send
multiple packets with a single syscall.  Higher values reduce syscall
overhead and generally give the server higher throughput and better
efficiency under high loads.

I believe that this is basically always a win under load when
supported, but values much larger than necessary do have a chance to
increase average response latency very slightly.  The optimal setting
is highly dependent on local hardware, software configuration, and
network load conditions.

Setting this to a value of 1 will completely disable this code, as if
we were running on a platform that didn't support it.  On platforms
that don't support it, this option has no effect and is ignored.  On
Linux if we don't detect a 3.0 or higher kernel at runtime, we fall
back to the same code as other platforms that don't support it.

=item B<udp_rcvbuf>

Integer, min 4096, max 1048576.  If set, this value will be used to set
the C<SO_RCVBUF> socket option on the UDP listening socket(s).  Most
users do not need to tune this value.  If left unset, the code only
takes a somewhat heuristic approach, trying to raise the value only if
the OS-supplied default seems too low, and multiplying it a bit in the
case of C<udp_recv_width> > 1.

=item B<udp_sndbuf>

Integer, min 4096, max 1048576.  If set, this value will be used to set
the C<SO_SNDBUF> socket option on the UDP listening socket(s).  Tuning
advice mirrors the above.

=item B<max_http_clients>

Integer, default 128, min 1, max 65535.  Maximum number of HTTP
connections to allow in parallel at any given time.  Once this number
is reached, no more new connections will be answered until an existing
connection closes or times out.

=item B<http_timeout>

Integer seconds, default 5, min 3, max 60.  HTTP connections will be
forcibly shut down if they go idle for more than this many seconds.

=item B<zones_strict_data>

Boolean, default C<false>

If false (the default), reporting of many less-serious errors in zone
data are emitted as mere logged warnings, and the zone data is still
loaded and served.

If this is set to true, such warnings will be upgraded and treated
the same as the more-serious class of zone data errors which prevent
successful loading of zone data.  The consequences of this are variable:
on initial startup or checkconf, this results in a fatal error (failure
to start, or non-zero exit code, respectively).  During a runtime
zone data reload, any existing good copy of the zone would continue
to be served until the error is corrected in the source.

=item B<zones_strict_startup>

Boolean, default C<true>

If true (the default), on daemon startup (via C<start> or C<restart>)
if any zone fails to load correctly, the daemon will
abort.  If false, the daemon will simply ignore the failed zone and
continue operations.

Runtime reloads via SIGHUP and/or periodic/inotify scanning always
treat bad zone data non-fatally (leaving any existing good copy intact
in memory for lookups).

This also affects the C<checkconf> action.  It will only fail in terms
of exit value on bad zonefiles if this is true (although it will note
any failures to stderr regardless).

=item B<zones_rfc1035_auto>

Boolean, default C<true>.

If auto is enabled (the default), the daemon will detect changes to
zone data automatically at runtime and apply them as they appear.  In
the general case this is done via periodically scanning C<lstat()> data
on the contents of the zones directory and looking for metadata changes
since last check.

On recent Linux systems, the daemon may also use C<inotify()> to detect
filesystem modifications in realtime and not need to run the periodic
full directory scan, making the average delay much smaller (subject to
compile- and run- time compatibility).  You will need a runtime Linux
kernel version of 2.6.36 or higher to enable this feature.

Regardless of whether this setting is true or false, you can always
manually trigger a rescan of the zones directory for new data by
sending the daemon a C<SIGHUP> (or executing the "reload" command /
initscript action, which sends SIGHUP for you).

=item B<zones_rfc1035_auto_interval>

Integer seconds, default 31, min 10, max 600.  Only applies when
C<zones_rfc1035_auto> is C<true>.

Sets the time interval for periodically checking the zonefile directory
for changes.  On systems which support C<inotify()>, however, the
automatic mode will almost always use that mechanism instead for even
faster detection with less overhead.  In the C<inotify()> case, the
interval is used only occasionally when recovering from temporary
C<inotify()> failures.

=item B<zones_rfc1035_min_quiesce>

Floating-point seconds, default 0.0, min 0.0, max 5.0

This short-duration quiescence timeout applies to certain internal
cases when validating zonefile update activity.  (Specifically: delays
after C<inotify()> events for atomic move/delete and delayed initial
zonefile loading on daemon startup).

At daemon start, a heuristic test of the mtime resolution on the zones
filesystem will determine whether we can use a faster 0.01s or the
default 1.02s as a basic sane minimum, and this config setting will be
adjusted upwards to the detected minimum as necessary.

Most users should not need to mess with this setting! The only reason
to do so would be if you suspected operating system or filesystem bugs
related to high-res mtimes (or bugs so severe that even ~1-second mtime
resolution isn't reliable, in which case you might want to try values
in the 3-5s range, or just find a new FS and/or OS...).

=item B<zones_rfc1035_quiesce>

Floating-point seconds, default 5.0, min 0.0, max 60.0

This timer is related to the above, but is used in cases where we're
not only worried about filesystem-level timestamp accuracy, but also
waiting for additional intentional actions by scripts, programs, or
users which might be actively modifying a zonefile.  It applies to all
changes detected via SIGHUP or periodic automatic scanning, and to
C<inotify()> events which do not indicate atomic operations (e.g.
create/write/close, rather than move/delete.  In other words,
someone/something is actually overwriting the data in-place or using an
editor on the file in-place).

It is recommended that whatever tools or scripts you use to manage
zonefile updates use atomic operations to replace them.  First write
the new data to a dotfile, e.g. F<.example.com.tmp1234>, in the same
zones directory (gdnsd ignores all filenames with a leading dot), and
then C<mv(1)> / C<rename(2)> the file to its final destination filename
F<example.com>.

If the value specified is less than the final runtime value of
C<zones_rfc1035_min_quiesce> above, it will be adjusted upwards to that
minimum value for correct operation.

=item B<lock_mem>

Boolean, default false.  Causes the daemon to do
C<mlockall(MCL_CURRENT|MCL_FUTURE)>, which effectively locks all daemon
memory into RAM, unable to be swapped.  Possibly helpful in some
production cases to ensure swap-in doesn't affect DNS latency.

When started as root with lock_mem set to true, the daemon will remove
any ulimits on locked memory before dropping privileges.  When started
as a regular user it may not be able to do so, and those limits could
cause the server to abort execution at any time if they are set too low.

=item B<priority>

Signed integer, range -20 to +20, lower values are higher priority.  If
explicitly set, gdnsd will attempt C<setpriority()> to this value on
startup.  If left unset and gdnsd is started as a normal user, no
C<setpriority()> call will be made.  If left unset and gdnsd is started
as root, it will default to calling C<setpriority()> with the value
C<-11>.

=item B<disable_text_autosplit>

Boolean, default false.  On the wire, C<TXT> (and C<SPF>, which are
identical in wire-format other than the RR-type) records are encoded as
discrete chunks of up to 255 characters per chunk.  The relevant RFCs
state that multiple chunks should be treated by clients as if they are
concatenated.  That is to say, it should make no difference to a client
whether the C<TXT> data is sent as two 16-byte chunks or one 32-byte
chunk.

Ordinarily, you may specify chunk(s) of a C<TXT> record in gdnsd
zonefiles as a string of any size up to the legal length (just short of
64K in practice), and gdnsd will auto-split the data into 255-byte
chunks for transmission over the DNS protocol correctly.  If you choose
to manually break up your TXT record into multiple strings in the
zonefile, gdnsd also honors these boundaries and will not attempt to
merge them into larger chunks where possible.

If you set this option to true, the auto-splitting behavior is
disabled, and any single character string specified in a zonefile as
part of a C<TXT> or C<SPF> record which is larger than 255 bytes will
be considered a syntax error.

=item B<include_optional_ns>

Boolean, default false.  Causes the daemon to include the optional NS
records in the Authority section of simple authoritative responses
containing actual response data.  Leaving this option in its default
state results in smaller response packets and faster response packet
generation in many common cases.  This is similar in nature to (but not
exactly like) BIND's "minimal-responses" option, except that we default
to the minimal mode.

Regardless of this setting, all *necessary* Authority-section records
are always included, such as when they are necessary for delegation
responses, NXDOMAIN responses, and NOERROR responses containing no
RRsets in the answer section.

=item B<plugin_search_path>

A single string or an array of strings, default empty.  Normally the
daemon searches for plugins in the fixed path C<$PREFIX/lib/gdnsd>,
using filenames of the form C<plugin_${name}.so>.  If you define this
parameter, all paths in this list will be searched in the given order
for plugins *before* trying the default, fixed search path.

=item B<realtime_stats>

Boolean, default false.  Normally the daemon self-imposes a limit of
not recalculating the daemon-wide statistics more often than once per
second.  This improves efficiency in the case that the polling traffic
on our HTTP interface gets high.

For most uses the default should be fine.  If you set this option to
true, the stats will be recalculated on the spot for every stats
request.  The test suite uses this so that it can double-check
statistics counters between every request it sends.  I don't imagine
anyone else will need to use this option, and it could even be
determinental to performance on SMP machines.

=item B<max_response>

Integer, default 16384, min 4096, max 62464.  This number is used to
size the per-I/O-thread buffers that we construct response packets in.
For any sane, normal use of the DNS, the default value is far more than
enough.  For embedded or other low memory hosts, you might even
consider setting this smaller than default to save a bunch of
per-socket-context buffer space.

However, if you have strange DNS data that's very large (giant RRsets,
giant blobs of data in TXT records) which might generate response
packets greater than the 16K default max here, you *must* set this
parameter large enough to accommodate them or random very bad things
will happen.  It should be noted that the odds are high whatever you're
trying to do is misguided in the first place.  You can size this by
setting it to the max and running some test queries via "dig" (or a
similar tool) to find your limit.

This number does not need to take into account UDP, IP, or any
lower-level headers.  Typically when probing your data for the largest
response sizes you should do C<ANY> queries and/or specific RR-type
queries against the first CNAME in any CNAME chains leading to large
RR-sets.  Keep in mind that the C<include_optional_ns> option will
affect the sizing as well.  Also keep in mind that wildcards and
delegations can match any child name, including ones of maximal overall
length.

=item B<max_addtl_rrsets>

Integer, default 64, min 16, max 256.  This is the maximum number of RR
sets that will ever be added to the Additional section of a response
packet.  This sets a hard limit on the number of delegation glue NS
records a subzone can have (which is checked at startup), and a runtime
soft limit on other Additional section RR sets.  When the limit is
reached at runtime, the remaining potential additional RR sets are
simply not added to the packet.  Most users won't need to raise this
value, and users on low-memory/embedded hosts might want to lower it to
save more memory.

=item B<max_cname_depth>

Integer, default 16, min 4, max 24.  How deep CNAME -> CNAME chains are
allowed to recurse within local data in a single zonefile.  If a chain
longer than this is detected between normal static CNAME entries in the
authoritative data of a single zonefile, an error will be thrown when
loading the zonefile.

If the limit is exceeded at runtime (due to C<DYNC> dynamic CNAME
responses) the code will halt further recursive lookups for this
request and return an empty B<NXDOMAIN> response, and log a loud
message to syslog on every single request for this broken domainname.

Note that this is the only thing preventing infinite CNAME loops caused
by bad DYNC plugin configurations.  Also note that even in the C<DYNC>
case, all of this applies only within a single zone.  The gdnsd code
never crosses the boundary between two distinct local zonefiles when
processing queries.

=item B<debug>

Boolean, default false.  If (and only if) gdnsd was built in debug mode
(--enable-developer, which slows things down with a ton of assertion
checks among other things), setting this option to C<true> will cause
additional debugging output to syslog/stderr.

=item B<edns_client_subnet>

Boolean, default true.  Enables support for the edns-client-subnet
option.  gdnsd only includes this EDNS option in responses to queries
which also contained the option.  In the case of normal responses from
static zone data, the scope mask will be set to zero.  Dynamic response
plugins have access to the query's EDNS client-subnet data, and have
full control over the response scope mask.

If the option is set to false, gdnsd will ignore the option in queries,
never set it in its responses, and plugins will not have access to any
data provided by any ignored edns-client-subnet option in queries.

Of the included standard plugins only C<reflect> and C<geoip> make use
of edns-client-subnet information.  The rest will leave the scope mask
at zero as normal for client-location-agnostic static data.

Relevant links documenting edns-client-subnet:

L<http://www.afasterinternet.com/>
L<http://tools.ietf.org/html/draft-vandergaast-edns-client-subnet-00>

=item B<monitor_force_v6_up>

Boolean, default false.  Forces all monitored resources with IPv6
addresses permanently to the UP state, and does not actually send them
monitoring requests.  Useful if some of your DNS servers don't have
working or reliable IPv6 routing, which would otherwise fail IPv6 polls
and force the related addresses to be marked DOWN.  A better
alternative would be to only host DNS for v6-capable services on
v6-capable DNS hosts, or install a Tunnelbroker/Sixxs/Teredo/Miredo/etc
tunnel to get v6 routability.

=item C<chaos_response>

String, default "gdnsd".  When gdnsd receives any query with the class
C<CH> ("Chaos"), as opposed to the normal C<IN> ("Internet"), it will
return a single response record of class C<CH> and type C<TXT>, which
contains the string defined here.  This is something like BIND's
version reporting, which responds to "version.bind" queries in the
C<CH> class, and is what a client will see if they use such a query
against a gdnsd server.

=back

=head1 SERVICE_TYPES

service_types is used in conjunction with certain gdnsd plugins.  If
you are not using such a plugin, you can safely ignore this section and
omit it from your configuration.

The service_types hash contains generic definitions for how to monitor
the given types of service.  Each service type uses a protocol-specific
plugin, and the default is the included plugin C<http_status>, which
checks HTTP status code responses.

The other included monitoring plugins are C<tcp_connect> (documented
below alongside C<http_status>, just checks TCP), and C<extmon>, which
executes external monitoring commands/scripts and has its own
documentation at L<gdnsd-plugin-extmon(8)>.

There are several generic parameters related to timing and anti-flap,
as well as plugin-specific parameters that vary per plugin.

A service type does not, however, specify a name or address for a
specific instance of a service.  Those would occur on a per-address
basis in a resolving plugin's configuration down in the C<plugins>
stanza, and the plugin's configuration would then reference a named
service type to be used when monitoring said address.

A service monitored through these mechanisms can be in one of three
states at runtime: C<UP>, C<DANGER>, or C<DOWN>.  The UP state means
that all is perfectly well.  The DANGER state means that some isolated
failures have been seen in the recent past (perhaps even just one), but
that gdnsd has not yet seen a consistent enough pattern of failure to
declare the service dead.  The DOWN state, obviously, means that gdnsd
does consider the service dead.  These states are presented to the
plugin that requested the monitoring.  It is up to the plugin to
determine how this affects DNS responses.

Any services monitored for plugins are also have their state reported
alongside the standard gdnsd statistics report, served by the built-in
HTTP server (default port is 3506).

There are five built-in service types that can't be overridden:

One built-in service type is implicitly named C<default>.  It uses the
default C<http_status> plugin and is defined to all of the default
parameters shown below.

The other four are named C<none>, C<up>, C<danger>, and C<down>.

These do no actual monitoring, and simply force the state of resources
using these service_types to a fixed state.  C<none> is just an alias
for C<up>.

The following are the generic parameters for all service_types:

=over 4

=item B<up_thresh>

Integer, default 20, min 1, max 255.  Number of monitoring requests
which must succeed with no intervening failures to transition a given
IP for this resource from the DOWN state to the UP state.

=item B<ok_thresh>

Integer, default 10, min 1, max 255.  Number of monitoring requests
which must succeed with no intervening failures to transition a given
IP for this resource from the DANGER state to the UP state.

=item B<down_thresh>

Integer, default 10, min 1, max 255.  Number of monitoring requests
which must fail, regardless of any intervening successes, to transition
a given IP for this resource from the DANGER state to the DOWN state.

=item B<interval>

Integer seconds, default 10, min 1, max 255.  Number of seconds between
successive monitoring requests to a given IP address for this resource.

=item B<timeout>

Integer seconds, default 3, min 1, max 255.  Maximum time the
monitoring code will wait for a successful response before giving up
and considering the request to be a failure.  Must be less than 90% of
B<interval>.

=item B<plugin>

String, default "http_status".  This indicates which protocol-specific
plugin to use to execute the monitoring requests.  Any parameters other
than the generic ones listed here are consumed by the plugin.

=back

The C<tcp_connect> plugin has just one plugin-specific parameter:

=over 4

=item B<port>

Integer, required.  This is the port number to contact on the remote
host to check this service type.  It has no default and must be
specified.

=back

The following are the plugin-specific parameters for the default
monitoring plugin C<http_status>:

=over 4

=item B<port>

Integer, default 80.  This is the port number to contact on the remote
host to check this service type.

=item B<url_path>

String, default C</>.  This is the URL that should be used when
checking this service type.

=item B<vhost>

Hostname, no default.  If defined, the HTTP/1.0 monitoring request will
include this as a C<Host:> header in the monitoring request.  If not
defined, no C<Host:> header will be sent.

=item B<ok_codes>

Array of 3-digit HTTP status codes, default C<[ 200 ]>.  This defines
the HTTP status codes in responses that will be accepted as successful.

=back

=head1 PLUGINS

The plugins hash is optional, and contains one key for every dynamic
resolution plugin you wish to load and use.  The value must be a hash,
and the contents of that hash are supplied to the plugin to use in
configuring itself.  If the plugin requires no configuration, the empty
hash C<{}> will suffice.  It is up to the plugin to determine whether
the supplied hash of configuration data is legal or not.

Monitoring-only plugins can also be given plugin-global level
configuration here if the plugin author deemed it necessary.

gdnsd ships with 3 very trivial dynamic resolution plugins named
C<null>, C<static>, and C<reflect>.

C<null> simply returns all-zeros addresses for DYNA records, and
C<invalid.> as the RHS of DYNC records.  It does not pay attention to
any plugin-specific configuration.

The C<static> plugin can be configured with a map of resource names to
IPv4 addresses or CNAME hostnames, and it will do the obvious: map
those resource names statically for use in either DYNA or DYNC zonefile
records.

C<reflect> is primarily for real-world testing and debugging.  It
attempts to reflect back to the query originator an address answer
showing the server's view of where the client is located on the
network.  Currently in the common case this will be the address of the
intermediate cache server which communicated directly with gdnsd.
However, if the query contains the draft edns-client-subnet option, the
response can reflect that as well.

It accepts 4 fixed resource names at the zonefile level: C<dns>,
C<edns>, C<best>, and C<both>.  C<dns> means to ignore
edns-client-subnet and always return the cache's address.  C<edns>
means to ignore the cache's address and always return
edns-client-subnet information, (or C<0.0.0.0> if not available).
C<best> will return the edns-client-subnet information if available, or
the cache's address if not.  C<both> returns both addresses if
edns-client-subnet is available, or just the cache otherwise.  The
default behavior is C<best>.

gdnsd also includes five other plugins that are more production-useful,
all of which have their own separate manpage documentation (e.g. C<man
gdnsd-plugin-FOO>):

=over 4

=item B<simplefo>

Simple primary->secondary failover of monitored addresses

=item B<multifo>

All-active failover of monitored round-robin address groups

=item B<weighted>

Weighted-round-robin responses with a variety of behavioral flavors,
for both monitored addresses and CNAMEs

=item B<metafo>

Static-ordered address(-group) meta-failover between 'datacenters',
which are resources defined in terms of other plugins

=item B<geoip>

Combines metafo's functionality with MaxMind GeoIP databases to select
different datacenter address(-group) preference/failover orderings for
different clients based on approximate geographic location.  Supports
geographically-differentiated CNAME resolution as well.

=back

A configuration example showing the trivial plugins, as well as
demonstrating the service_types described earlier:

  service_types => {
    corpwww_type => {
      vhost => www.corp.example.com
      url_path => /check_me
      down_thresh => 5
      interval => 5
    }
  }

  plugins => {
    null => {},
    reflect => {},
    static => {
      foo = 192.0.2.2
      bar = 192.0.2.123
      somehost = somehost.example.net.
    },
  }

And then in your example.com zonefile, you could have (among your other
RRs):

  zeros 600 DYNA null
  reflect 10 DYNA reflect
  reflect-both 10 DYNA reflect!both
  pointless 42 DYNA static!foo
  acname 400 DYNC static!somehost

=head1 LOW-LEVEL SYNTAX

At the lowest level, the syntax of gdnsd config files roughly resembles
an anonymous Perl data structure (using reference syntax).  There are
three basic data types for values: ordered hashes (associative arrays
mapping keys to values), ordered arrays of values, and simple strings.
Hashes and arrays can be nested to arbitrary depth.  Generally
speaking, whitespace is optional.  Single-line comments in both shell
(C<#>) and DNS zonefile styles (C<;>) are allowed.  They run to the end
of the current line and are considered to be whitespace by the parser.

A hash is surrounded by curly braces (C<{> and C<}>).  Keys are
separated from their values by either C<=E<gt>> or C<=> (at your
stylistic discretion).  Hash keys follow the same rules as simple
string values.  Hash values can be simple strings, arrays, or hashes.
Key/value pairs can optionally have a trailing comma for stylistic
clarity and separation.

An array is surrounded by square braces (C<[> and C<]>).  Values can be
simple strings, arrays, or hashes.  Values can optionally have a
trailing comma for style.

Strings (and thus keys) can be written in both quoted and unquoted
forms.  In the quoted form, the string is surrounded by double-quotes
(C<">), and can contain any literal byte value (even binary/utf-8
stuff, or NUL) other than C<"> or C<\>.  Those two characters must be
escaped by C<\>, i.e.  C<\"> and C<\\>.

In the unquoted form, there are no surrounding quotes, and the allowed
set of unescaped characters is further restricted.  The following are
not allowed: C<][}{;#,"=\> (that is, square brackets, curly brackets,
semicolons, octothorpes, commas, double quotes, equal signs, and
backslashes).  Additionally, the first character cannot be a C<$>
(dollar sign).

Both forms use the same escaping rules, which are the same RFC-standard
escaping rules used in zone files.  The escapes always start with C<\>.
C<\> followed by any single byte other than a digit (C<0> - C<9>) is
interepreted as that byte.  C<\> followed by exactly 3 digits
interprets those digits as the unsigned decimal integer value of the
desired byte (the 3 digit value cannot exceed C<255>).

To illustrate the escaping and quoting, the following sets of example
strings show different encodings of the same parsed value:

  example
  "example"
  ex\097mpl\e
  "ex\097mpl\e"

  internal\"doublequote
  "internal\"doublequote"

  white\ space
  "white space"

  "braces{every[where]oh}my"
  braces\{every\[where\]oh\}my

  "\\==="
  "\092==="
  "\092\=\=\="
  \\\=\=\=
  \092\=\=\=

The top level of the config file is an implicit hash with no bracing
by default, but can also be an array bounded by square brackets.  This
is not legal for the primary gdnsd configuration file, but could be
useful in includefiles (see below).

As a general rule, anywhere the higher-level syntax allows an array of
values, you can substitute a single value.  The code will treat it as
if it were an array of length 1.

When we refer in other sections above to a value as being an "Integer"
(or other specific scalar type), we're referring to constraints on the
content of the character string value.  All scalar values are character
strings.  "Boolean" values are characters strings which have the value
"true" or "false", in any mix of upper or lower case.

The following 3 example configuration files are identical in their
parsed meanings, and should clarify anything miscommunicated above:

Example 1 (simple and clean):

  options = {
    listen = [ 192.0.2.1, 192.0.2.2 ],
    http_listen = 127.0.0.1,
  }

Example 2 (fat arrows, no commas, some arbitrary quoting):

  "options" => {
    listen => [ 192.0.2.1 192.0.2.2 ]
    http_listen => "127.0.0.1"
  }

Example 3 (compressed and ugly):

  options={listen=[192.0.2.1 192.0.2.2]http_listen=127.0.0.1}

=head1 INCLUDING OTHER FILES

vscf now has a mechanism for config includefiles.  The syntax is

  $include{filename}

where C<filename> can use the same kinds of escaping and/or
double-quoting as normal scalar string data.  Whitespace between
the filename and the surrounding brackets is optional.  Whitespace
between C<$include> and the following C<{> is not.  If the filename
is relative (does not begin with F</>), it is interpreted as
relative to the directory containing the parent file.  Include files
can nest other include files to arbitrary depth.

Keep in mind that at the top level of any given vscf file (even
include files), the file must syntactically be either an implicit
hash or an explicit, square-bracket-bounded, array.

The include statement can be used in two distinct contexts within
the syntax structure of a config file:

=over 4

=item B<Value Context>

The include statement can replace any whole value (that is, the right
hand side of a hash map entry or a member of an array) with its own
contents, which are either a hash or an array.  Note that there is no
mechanism for flattening an include-file's array into the parent array
(the whole included array would be a single array item within the parent
array).  Examples:

  main config:
    options => { listen => $include{foo} }
  foo:
    [ 127.0.0.1, 127.0.0.2 ]

  main config:
    plugins => $include{ "bar" }
  bar:
    geoip => { ... }
    extmon => { ... }

=item B<Hash-Merge Context>

The include statement can also appear in a hash where a key would
normally be expected.  In this case, the included file must be in hash
(rather than array) form at the top level, and its contents are merged
into the parent hash.  Example:

  main config:
    options => { ... },
    plugins => {
        extmon => { ... },
        metafo => { ... },
        $include{geoip_cfg},
        simplefo => { ... }
    }
  geoip_cfg:
    geoip => { ... },
    weighted => { ... }

=back

=head1 SEE ALSO

L<gdnsd(8)>, L<gdnsd.zonefile(5)>, L<gdnsd-plugin-simplefo(8)>,
L<gdnsd-plugin-multifo(8)>, L<gdnsd-plugin-weighted(8)>,
L<gdnsd-plugin-metafo(8)>, L<gdnsd-plugin-geoip(8)>,
L<gdnsd-plugin-extmon(8)>, L<gdnsd-plugin-api(3)>

The gdnsd manual.

=head1 COPYRIGHT AND LICENSE

Copyright (c) 2012 Brandon L Black <blblack@gmail.com>

This file is part of gdnsd.

gdnsd is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

gdnsd is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with gdnsd.  If not, see <http://www.gnu.org/licenses/>.

=cut
