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 => {}
}
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: options, service_types, and 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.
These options control the overall behavior of gdnsd(8).
A listen-address specification is an IP (v4 or v6) address specified as a numeric string with standard formatting (anything numeric that "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 "dns_port", which defaults to 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 "tcp_threads", "tcp_timeout", "tcp_clients_per_thread", "udp_threads", "udp_recv_width", "udp_rcvbuf", and "udp_sndbuf".
There are also two special singalur string values: "any" and "scan".
If set to "any", the daemon will listen on the "dns_port" port (default 53) on 0.0.0.0 and "::" (if IPv6 support is detected).
options => { listen => any }
If set to "scan", scan all available IP (v4 and v6) network interfaces via "getifaddrs()" and set up a separate listener on the "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 "scan". However, this default is expected to change to "any" in a future version, so you should specify "scan" explicitly if you require this behavior.
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.
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.
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.
If true (the default), on daemon startup (via "start" or "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 "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).
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 "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 "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 "SIGHUP" (or executing the "reload" command / initscript action, which sends SIGHUP for you).
Sets the time interval for periodically checking the zonefile directory for changes. On systems which support "inotify()", however, the automatic mode will almost always use that mechanism instead for even faster detection with less overhead. In the "inotify()" case, the interval is used only occasionally when recovering from temporary "inotify()" failures.
This short-duration quiescence timeout applies to certain internal cases when validating zonefile update activity. (Specifically: delays after "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...).
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 "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. .example.com.tmp1234, in the same zones directory (gdnsd ignores all filenames with a leading dot), and then mv(1) / rename(2) the file to its final destination filename example.com.
If the value specified is less than the final runtime value of "zones_rfc1035_min_quiesce" above, it will be adjusted upwards to that minimum value for correct operation.
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.
Ordinarily, you may specify chunk(s) of a "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 "TXT" or "SPF" record which is larger than 255 bytes will be considered a syntax error.
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.
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.
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 "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 "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.
If the limit is exceeded at runtime (due to "DYNC" dynamic CNAME responses) the code will halt further recursive lookups for this request and return an empty 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 "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.
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 "reflect" and "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:
<http://www.afasterinternet.com/> <http://tools.ietf.org/html/draft-vandergaast-edns-client-subnet-00>
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 "http_status", which checks HTTP status code responses.
The other included monitoring plugins are "tcp_connect" (documented below alongside "http_status", just checks TCP), and "extmon", which executes external monitoring commands/scripts and has its own documentation at 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 "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: "UP", "DANGER", or "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 "default". It uses the default "http_status" plugin and is defined to all of the default parameters shown below.
The other four are named "none", "up", "danger", and "down".
These do no actual monitoring, and simply force the state of resources using these service_types to a fixed state. "none" is just an alias for "up".
The following are the generic parameters for all service_types:
The "tcp_connect" plugin has just one plugin-specific parameter:
The following are the plugin-specific parameters for the default monitoring plugin "http_status":
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 "{}" 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 "null", "static", and "reflect".
"null" simply returns all-zeros addresses for DYNA records, and "invalid." as the RHS of DYNC records. It does not pay attention to any plugin-specific configuration.
The "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.
"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: "dns", "edns", "best", and "both". "dns" means to ignore edns-client-subnet and always return the cache's address. "edns" means to ignore the cache's address and always return edns-client-subnet information, (or 0.0.0.0 if not available). "best" will return the edns-client-subnet information if available, or the cache's address if not. "both" returns both addresses if edns-client-subnet is available, or just the cache otherwise. The default behavior is "best".
gdnsd also includes five other plugins that are more production-useful, all of which have their own separate manpage documentation (e.g. "man gdnsd-plugin-FOO"):
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
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 ("#") and DNS zonefile styles (";") 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 ("{" and "}"). Keys are separated from their values by either "=>" or "=" (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 ("[" and "]"). 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 ("""), and can contain any literal byte value (even binary/utf-8 stuff, or NUL) other than """ or "\". Those two characters must be escaped by "\", i.e. "\"" and "\\".
In the unquoted form, there are no surrounding quotes, and the allowed set of unescaped characters is further restricted. The following are not allowed: "][}{;#,"=\" (that is, square brackets, curly brackets, semicolons, octothorpes, commas, double quotes, equal signs, and backslashes). Additionally, the first character cannot be a "$" (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 "\". "\" followed by any single byte other than a digit (0 - 9) is interepreted as that byte. "\" followed by exactly 3 digits interprets those digits as the unsigned decimal integer value of the desired byte (the 3 digit value cannot exceed 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}
vscf now has a mechanism for config includefiles. The syntax is
$include{filename}
where "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 $include and the following "{" is not. If the filename is relative (does not begin with /), 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:
main config:
options => { listen => $include{foo} }
foo:
[ 127.0.0.1, 127.0.0.2 ]
main config:
plugins => $include{ "bar" }
bar:
geoip => { ... }
extmon => { ... }
main config:
options => { ... },
plugins => {
extmon => { ... },
metafo => { ... },
$include{geoip_cfg},
simplefo => { ... }
}
geoip_cfg:
geoip => { ... },
weighted => { ... }
gdnsd(8), gdnsd.zonefile(5), gdnsd-plugin-simplefo(8), gdnsd-plugin-multifo(8), gdnsd-plugin-weighted(8), gdnsd-plugin-metafo(8), gdnsd-plugin-geoip(8), gdnsd-plugin-extmon(8), gdnsd-plugin-api(3)
The gdnsd manual.
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/>.