GDNSD(8)

✨ GDNSD ✨

okay bestie, welcome to the gdnsd(8) manual page β€” this is the section-8 entry for gdnsd, and it's literally your new favorite thing. she's an authoritative DNS daemon. she's fast. she's light. she's pluggable. she's the main character πŸ’…


🌸 NAME 🌸

gdnsd β€” An authoritative DNS daemon πŸŽ€


πŸ¦‹ SYNOPSIS πŸ¦‹

Usage: gdnsd [-c /etc/gdnsd] [-D] [-l] [-S] [-R | -i] <action>
  -c - Configuration directory (default /etc/gdnsd)
  -D - Enable verbose debug output
  -l - Send logs to syslog rather than stderr
  -S - Force 'zones_strict_data = true' for this invocation
  -R - Attempt downtimeless replacement of another instance
  -i - Idempotent mode for start/daemonize: exit 0 if already running
       (-R and -i cannot be used together)
Actions:
  checkconf - Checks validity of config and zone files
  start - Start as a regular foreground process
  daemonize - Start as a background daemon (implies -l)

like, that's the whole shape of it. flags up top, action at the end, no cap πŸ‘‘


πŸ’– DESCRIPTION πŸ’–

gdnsd is a very fast, light, and pluggable authoritative DNS daemon. periodt.


πŸŽ€ BASIC CONFIGURATION πŸŽ€

so the primary configuration file is the file named config β€” and it lives in the configuration directory. that's the vibe.

not me about to tell you something wild: the config file does not have to exist for a successful startup. nope. if there's no config file at all, gdnsd will just load all of the zones in the zones directory (default "/etc/gdnsd/zones/") and listen on port 53 of 0.0.0.0 and "::" using default settings. she's lowkey self-sufficient like that 🫢

✨ COMMANDLINE OPTION FLAGS ✨

  • -c β€” Sets the configuration directory, which defaults to /etc/gdnsd. the zones directory is the zones/ subdirectory of the configuration directory. iykyk.
  • -D β€” Enables additional debug-level log output, as appropriate. she's telling you everything πŸ‘€
  • -l β€” Sends log output to syslog rather than the default stderr. this gets implicitly turned on by the "daemonize" action, and it's recommended when you're running as a systemd service too, so you capture priority information.
  • -S β€” Forces the "zones_strict_data" configuration option to true for this invocation, no matter what the config file says. mostly this is useful for validation during the "checkconf" option.
  • -R β€” Replace mode for "start" and "daemonize":

normally, if another server instance has the server control socket locked, a "start" or "daemonize" attempt will just fail. but with this flag set, the new server connects to the old one over its control socket and attempts a downtime-less, graceful takeover of operations. this lets both code and configuration changes happen without losing requests in most cases, and in general it's fairly seamless. πŸ’« check the gdnsdctl(8) docs for "replace" for more info, because that's the preferred interface to this functionality.

  • -i β€” Idempotent mode for "start" and "daemonize":

normally, if another server instance has the server control socket locked, a "start" or "daemonize" attempt will fail. with this flag set, if another instance is already running, this instance will report success and exit with status zero immediately. no drama, no fuss 😌

heads up bestie: -R and -i cannot be used together. pick one.

πŸ‘‘ ACTIONS πŸ‘‘

  1. checkconf β€” Checks the validity of the configuration file and zonefiles, and sets the exit status appropriately (0 for success). the same code executes implicitly during startup; checkconf just exits at the last point before real runtime actions need to be taken, like acquiring sockets. she's the vibe check before the party πŸŽ‰
  2. start β€” Starts gdnsd as a runtime DNS server foreground process, emitting log output to stderr.
  3. daemonize β€” Starts gdnsd as a detached background daemonize process in a new session, parented to PID 1 (init), emitting log output directly to syslog. the initial foreground process will emit no terminal output on success, but it will wait for the background daemon to complete startup operations successfully before exiting with status zero (or not β€” in which case it exits non-zero and emits some kind of failure output to either stderr or syslog). she's patient like that 🧚

any other commandline option gets treated as invalid, which means you get a short help text dumped to STDERR and a non-zero exit status. and yes, that includes the ubiquitous --help and --version. πŸ’€ sorry bestie, she does not do those.


πŸ’œ PRIVILEGES πŸ’œ

okay so this service pays no attention to β€” and doesn't attempt to check or modify β€” anything about the runtime security environment or privilege set. she also doesn't care whether or not she happens to be running as "root", and she doesn't attempt any actions that would commonly require "root" privileges, like altering her own memlock rlimits or raising her own process priority. nope, not her thing.

the lone exception is that the code blindly assumes that binding its DNS listening ports β€” which default to being in the privileged range β€” will Just Work. 🫠

she expects all such things to be managed by external mechanisms: either a systemd-like init system that can do it all declaratively, or a combination of tooling in an initscript for a traditional init system, to take care of the necessary privileged settings and/or security-jailing / ACLs, etc,


🌈 SYSTEMD COMPATIBILITY 🌈

when you run her as a systemd service, use "start" rather than "daemonize", use the "-l" flag, and set the type to "Type=notify" with "NotifyAccess=all". those latter bits make systemd set $NOTIFY_SOCKET for the daemon, and the daemon in turn uses this to send a readiness notification when she's done with initial startup tasks and ready to service requests. and for the "ExecStop=" command, use "gdnsdctl -l stop". ✨

the source tree contains an example systemd unit file with reasonable defaults for a production deployment. slay.


πŸ“ ZONE FILES - RFC1035 πŸ“

the directory for standard RFC1035 zone files (which is the default zone data backend) is the subdirectory named zones/ in the configuration directory β€” so the default would be /etc/gdnsd/zones/.

RFC1035 zone files are the traditional zone file format, the kind you typically use with e.g. BIND. for more info on the internal format and processing of these files, go read gdnsd.zonefile(5). this section is about how the directory itself is managed, though.

here's the tea:

  • All files in the zones directory are considered zone files. in general there should be exactly one file per zone, and the filename should match the zone name.
  • Filenames beginning with . are ignored. πŸ™ˆ
  • All zone files must be regular files β€” as opposed to directories, symlinks, sockets, etc.

to better support the special case of RFC 2137-style classless in-addr.arpa delegation zones (which contain forward slashes), any "@" symbol in the filename gets translated to a forward slash ("/") when transforming a filename into its corresponding zone name.

for similar reasons, if your server is intended to serve the root of the DNS, the filename for the root zone should be the special filename ROOT_ZONE, rather than the impossible literal filename .. because authoritative servers cannot serve two domains which have a parent<->child relationship correctly, a root server cannot serve any other zone, so this would be the sole zonefile. πŸ‘‘

the standard DNS zone file escape sequences are recognized within the filenames (e.g. "\." for a dot within a label, or "\NNN" where NNN is a decimal integer in the range 0 - 255), if for some reason you need a strange character in your zone name.

trailing dots on zonefile names are ignored; e.g. example.com and example.com. are functionally equivalent. ✨

illegal, bestie: - Duplicate zones β€” e.g. having both of the above representations of "example.com" present in the zones directory, and/or adding a different case-mapping such as EXample.Com. - Subzones β€” e.g. having zonefiles for both "example.com" and "subz.example.com".

πŸ¦‹ ZONE FILES - RELOADING πŸ¦‹

"gdnsdctl reload-zones" triggers re-loading all zone data. the reload operation is done in a separate thread from the main server and doesn't interrupt DNS request flow. she multitasks, it's giving main character energy πŸ”₯

"gdnsdctl" will wait for the server to complete the load operation and report back the success or failure of the reload transaction via stdout and its process exit status.

if any failure occurs during the reload operation, the entire reload transaction is discarded with no effect on the previous runtime data that's still in use to answer ongoing queries. she's got your back, fr fr. 🫢


πŸŽ€ DIRECTORIES πŸŽ€

important directory paths for the core daemon code:

  • /etc/gdnsd β€” Default configuration directory, unless overridden via "-c". the primary configuration file is always the file config in the configuration directory.
  • /run/gdnsd β€” Default run_dir. this is where the daemon's control socket and lock files are created. see the entry for "run_dir" in the gdnsd.config(5) manpage for more info about this directory.
  • /var/lib/gdnsd β€” Default state_dir. the admin_state file is read from this directory for administrative state-overrides on monitored resources, see below in the FILES section. see the entry for "state_dir" in the gdnsd.config(5) manpage for more info about this directory.
  • /usr/libexec/gdnsd β€” This is the default path for daemon-private executables that users should not run. the only current case is gdnsd_extmon_helper for the extmon plugin, and the path for this can be overridden in that plugin's configuration, documented in gdnsd-plugin-extmon(8).

πŸŽ€ ADMIN STATE FILE - /var/lib/gdnsd/admin_state πŸŽ€

Okay bestie, this file? This is your file. This is the one where you get to be the boss πŸ’…

This file is the input for administrative state overrides that affect plugin resolution decisions. The whole point of it is to let explicit, human administrative decisions temporarily override the states that affect plugin decision-making when it comes to failover and/or geographic distribution. A file that doesn't exist at all gets treated exactly the same as an empty file, no drama. The file is watched at runtime for changes, and any overridden state it finds gets applied quickly. And here's the vibe: the file is expected to persist across reboots and daemon restarts, so your intent as the administrator survives all of that. Your word is law, basically πŸ‘‘

A basic understanding of how both monitoring plugins and resolution plugins in gdnsd work is assumed (peep gdnsd.config(5) for that). This file gets parsed as a vscf hash data structure (again, gdnsd.config(5) has the deep lore on that format). The keys are the names of monitored or virtual resources, and the values are forced state values (and optionally monitored-TTL values too, we love options). Keys can also be wildcards using shell glob syntax, which means one key can affect multiple resources at once. Group chat energy, fr fr.

For normal monitored resources, the typical form of a key is "THING/service_type", where "THING" is the monitored address or CNAME value and "service_type" is the service_type configured to monitor that address or CNAME value by one or more resolver plugins. The value portion takes the form "STATE[/TTL]", where "STATE" is "UP" or "DOWN" and the TTL portion is an optional override of the monitored TTL.

The order of the lines in the file is important! They get processed and applied in-order, so later lines can override the actions of earlier lines. This is especially handy for making exceptions to glob-matches. First come, first served β€” except the last one wins. Make it make sense 😌

Example:

/var/lib/gdnsd/admin_state:
    2001:db8::2:123/my_http_check => DOWN # down a specific res+stype
    foo.example.com./extmon_ping => UP # up a specific res+stype
    192.0.2.1/* => DOWN # down all service_types for this address
    */xmpp => UP/30 # up all resources monitored by xmpp w/ TTL 30 ...
    192.0.2.2/xmpp => DOWN # ... except this one

Some resolution plugins can also register virtual resources (which are not monitored by any "service_type") purely for the purpose of administrative override of decision-making. Right now the geoip and metafo plugins do this for their "datacenters", and the keys they create take the form "plugin_name/resname/dcname" to force a datacenter's state at the per-resource level. The geoip plugin also supports keys of the form "plugin_name/mapname/dcname" to force a datacenter's state at the per-map level. These forcings override the aggregate state passed up to geoip/metafo from per-datacenter plugins (like multifo or weighted monitoring several addresses in a datacenter, iykyk), and in the geoip case the more-specific per-resource forced state will override any per-map forced state. Most specific wins, that's just the rules πŸ’…

Example:

/var/lib/gdnsd/admin_state:
    geoip/map3/dc-us => DOWN # down dc-us in geoip map3
    */dc-jp => DOWN # down all datacenters named dc-jp for geoip and metafo
    metafo/res_www/dc-jp => UP # exception to above

All of the available monitored and virtual keys that can be matched in this file, plus their current monitored and/or forced states, are visible in the output of gdnsdctl states in JSON format. Go peek, bestie πŸ‘€


🌈 SIGNALS 🌈

Any signal that isn't explicitly mentioned here is not explicitly handled. Which is a fancy way of saying: they'll do their default actions, and those default actions often include aborting execution πŸ’€ So like. Be careful.

  • SIGTERM, SIGINT β€” Causes the daemon to exit gracefully, with accompanying log output. The exit status will reflect the terminating signal appropriately. Clean exit, we love to see it 🫢
  • SIGPIPE β€” Ignored. Not today, not ever. πŸ™ˆ
  • SIGHUP β€” Okay this one's got nuance. If started via daemonize, SIGHUP is explicitly ignored by the background daemon, but not by the temporary foreground process during initialization. When started via start, SIGHUP handling is left at defaults, which will normally result in unclean process termination on reception. So yeah, rude awakening if you send it at the wrong moment 😭
  • SIGUSR1 β€” This signal is "handled" by the main daemon for compatibility reasons, because previous versions used it to request asynchronous zone data reloads. It just logs an error now that points you at the new gdnsdctl mechanism, and has no real effect. This compatibility handling may be removed in a future major version update. It's giving legacy ✨
  • SIGUSR2 β€” Used internally as an inter-thread signal, with no documented use from the outside. Do not send this signal to any thread of the daemon. Fr fr, hands off πŸ”₯

πŸ’– EXIT STATUS πŸ’–

An exit status of zero means success. Anything else means failure. That's it, that's the whole philosophy πŸŽ€

For daemonize, the exit status of the initial foreground process will indicate whether the background daemon launched successfully. Basically the parent is like "yeah she made it" or "nope" and dips.

For start, the initial foreground process is the daemon, and the exit status will reflect the terminating signal appropriately even in a clean shutdown case, or be zero if it was cleanly shut down by non-signal mechanisms. Main character energy, honestly πŸ‘‘


🌸 SEE ALSO 🌸

The gdnsd manual. Go read it, bestie, it's free education πŸ“šβœ¨


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/>.


And that's the whole page, bestie! This was gdnsd version 3.8.3, dated 2025-02-16, from GDNSD(8) β€” go forth and resolve some DNS, you absolute icon πŸ¦‹πŸ’œ
πŸ“¦ where this page comes from