From 7f5fa15df3b3df974f972dd8f659427bdbf88122 Mon Sep 17 00:00:00 2001 From: Jan Broer Date: Sun, 24 Jan 2016 03:23:01 +0100 Subject: [PATCH 1/5] Documentation nits --- README.md | 56 ++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 35 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 39e0676..a121ad4 100644 --- a/README.md +++ b/README.md @@ -1,35 +1,35 @@ # go-dnsmasq *Version 0.9.8* -go-dnsmasq is a light weight (1.2 MB) DNS caching server/forwarder with minimal filesystem and runtime overhead. It is designed to serve global DNS records by forwarding queries to upstream nameservers as well as local hostname records from a hostsfile. +go-dnsmasq is a light weight (1.2 MB) DNS caching server/forwarder with minimal filesystem and runtime overhead. ### Application examples: -- as local DNS cache for Docker containers -- as nameserver providing local and global DNS records to clients in a private networks -- as DNS proxy providing `search` domain path capability to `musl-libc` based clients (e.g. Alpine Linux) +- Caching DNS server/forwarder in a local network +- Container/Host DNS cache +- DNS proxy providing DNS `search` capabilities to `musl-libc` based clients, particularly Alpine Linux ### Features -* Parses upstream nameservers from resolv.conf -* Configures itself as local DNS cache in resolv.conf -* Serves static hostname records from a hostsfile -* Caching of answers -* Replicates the `search` domain suffixing not supported by `musl-libc` based Linux distributions. -* Stubzones (use a different nameserver for specific domains) -* Round-robin of address records -* Sending stats to Graphite server -* Configuration through CLI and environment variables +* Automatically set upstream `nameservers` and `search` domains from resolv.conf +* Automatically set go-dnsmasq as primary nameserver for the host it is running on +* Serve static records from a hostsfile +* Provide DNS answer caching +* Replicate the `search` domain treatment not supported by `musl-libc` based Linux distributions +* Configure stubzones (different nameserver for specific domains) +* Round-robin of DNS records +* Send server metrics to Graphite and StatHat +* Configuration through both command line flags and environment variables -### Resolver logic +### Resolve logic -DNS queries are processed according to the logic used by the GNU C resolver library: -* The first nameserver (as listed in resolv.conf or configured by `--nameservers`) is considered the primary server. Additional servers are queried only when the primary server times out or returns an error code. -* Multiple `search` paths are tried in the order they are configured. -* Single-label queries (e.g.: "redis-service") will always be qualified with `search` list elements -* For multi-label queries (ndots >= 1) the name will be tried first as an absolute name before any `search` list elements are appended to it. +DNS queries are resolved in the style of the GNU libc resolver: +* The first nameserver (as listed in resolv.conf or configured by `--nameservers`) is always queried first, additional servers are considered fallbacks +* Multiple `search` domains are tried in the order they are configured. +* Single-label queries (e.g.: "redis-service") are always qualified with the `search` domains +* Multi-label queries (ndots >= 1) are first tried as absolute names before qualifying them with the `search` domains -### Commandline options +### Command-line options ```sh NAME: @@ -67,7 +67,21 @@ GLOBAL OPTIONS: ### Environment Variables -See above (the names inside the brackets). +See command-line options. + +##### Enable Graphite/StatHat metrics using environment variables + +EnvVar: **GRAPHITE_SERVER** +Default: ` ` +Set to the `host:port` of the Graphite server + +EnvVar: **GRAPHITE_PREFIX** +Default: `go-dnsmasq` +Set a custom prefix for Graphite metrics + +EnvVar: **STATHAT_USER** +Default: ` ` +Set to your StatHat account email address ### Usage From 83266ceee809dfeff55ce84b0e1fb424264b849f Mon Sep 17 00:00:00 2001 From: Jan Broer Date: Wed, 27 Jan 2016 15:14:37 +0100 Subject: [PATCH 2/5] Use markdown table --- README.md | 71 +++++++++++++++++++++---------------------------------- 1 file changed, 27 insertions(+), 44 deletions(-) diff --git a/README.md b/README.md index a121ad4..749e1c1 100644 --- a/README.md +++ b/README.md @@ -12,9 +12,9 @@ go-dnsmasq is a light weight (1.2 MB) DNS caching server/forwarder with minimal ### Features * Automatically set upstream `nameservers` and `search` domains from resolv.conf -* Automatically set go-dnsmasq as primary nameserver for the host it is running on -* Serve static records from a hostsfile -* Provide DNS answer caching +* Insert itself into the host's /etc/resolv.conf on start +* Serve static A/AAAA records from a hostsfile +* Provide DNS response caching * Replicate the `search` domain treatment not supported by `musl-libc` based Linux distributions * Configure stubzones (different nameserver for specific domains) * Round-robin of DNS records @@ -29,47 +29,30 @@ DNS queries are resolved in the style of the GNU libc resolver: * Single-label queries (e.g.: "redis-service") are always qualified with the `search` domains * Multi-label queries (ndots >= 1) are first tried as absolute names before qualifying them with the `search` domains -### Command-line options - -```sh -NAME: - go-dnsmasq - Lightweight caching DNS proxy for Docker containers - -USAGE: - go-dnsmasq [global options] command [command options] [arguments...] - -VERSION: - 0.9.8 - -COMMANDS: - help, h Shows a list of commands or help for one command - -GLOBAL OPTIONS: - --listen, -l "127.0.0.1:53" listen address: ‘host[:port]‘ [$DNSMASQ_LISTEN] - --default-resolver, -d make go-dnsmasq the local primary nameserver (updates /etc/resolv.conf) [$DNSMASQ_DEFAULT] - --nameservers, -n comma-separated list of name servers: ‘host[:port]‘ [$DNSMASQ_SERVERS] - --stubzones, -z domains to resolve using a specific nameserver: ‘fqdn[,fqdn]/host[:port]‘ [$DNSMASQ_STUB] - --hostsfile, -f full path to hostsfile (e.g. ‘/etc/hosts‘) [$DNSMASQ_HOSTSFILE] - --hostsfile-poll, -p "0" how frequently to poll hostsfile (in seconds, ‘0‘ to disable) [$DNSMASQ_POLL] - --search-domains, -s specify SEARCH domains taking precedence over /etc/resolv.conf: ‘fqdn[,fqdn]‘ [$DNSMASQ_SEARCH] - --append-search-domains, -a enable suffixing single-label queries with SEARCH domains [$DNSMASQ_APPEND] - --rcache, -r "0" capacity of the response cache (‘0‘ to disable caching) [$DNSMASQ_RCACHE] - --rcache-ttl "60" TTL of entries in the response cache [$DNSMASQ_RCACHE_TTL] - --no-rec disable recursion [$DNSMASQ_NOREC] - --round-robin enable round robin of A/AAAA replies [$DNSMASQ_RR] - --systemd bind to socket(s) activated by systemd (ignores --listen) [$DNSMASQ_SYSTEMD] - --verbose enable verbose logging [$DNSMASQ_VERBOSE] - --syslog enable syslog logging [$DNSMASQ_SYSLOG] - --multithreading enable multithreading (num physical CPU cores) [$DNSMASQ_MULTITHREADING] - --help, -h show help - --version, -v print the version -``` - -### Environment Variables - -See command-line options. - -##### Enable Graphite/StatHat metrics using environment variables +### Command-line options / environment variables + +| Flag | Description | Default | Environment vars | +| ------------------------------ | ----------------------------------------------------------------------------- | ------------- | -------------------- | +| --listen, -l | Address to listen on `host[:port]` | 127.0.0.1:53 | $DNSMASQ_LISTEN | +| --default-resolver, -d | Insert itself into /etc/resolv.conf | False | $DNSMASQ_DEFAULT | +| --nameservers, -n | Comma-separated list of nameservers `host[:port]` | - | $DNSMASQ_SERVERS | +| --stubzones, -z | Use different nameservers for specific domains `fqdn[,fqdn]/host[:port]` | - | $DNSMASQ_STUB | +| --hostsfile, -f | Full path to a hostsfile | - | $DNSMASQ_HOSTSFILE | +| --hostsfile-poll, -p | How frequently to check hostsfile for changes (seconds, ‘0‘ to disable) | 0 | $DNSMASQ_POLL | +| --search-domains, -s | Specify SEARCH domains (takes precedence over /etc/resolv.conf) `fqdn[,fqdn]` | - | $DNSMASQ_SEARCH | +| --append-search-domains, -a | Qualify queries with SEARCH domains | False | $DNSMASQ_APPEND | +| --rcache, -r | Capacity of the response cache (‘0‘ to disable cache) | 0 | $DNSMASQ_RCACHE | +| --rcache-ttl | TTL for entries in the response cache | 60 | $DNSMASQ_RCACHE_TTL | +| --no-rec | Disable recursion | False | $DNSMASQ_NOREC | +| --round-robin | enable round robin of A/AAAA records | False | $DNSMASQ_RR | +| --systemd | Bind to socket(s) activated by Systemd (ignores --listen) | False | $DNSMASQ_SYSTEMD | +| --verbose | Enable verbose logging | False | $DNSMASQ_VERBOSE | +| --syslog | Log to syslog | False | $DNSMASQ_SYSLOG | +| --multithreading | Enable multithreading | False | | +| --help, -h | Show help | | | +| --version, -v | Print the version | | | + +#### Enable Graphite/StatHat metrics EnvVar: **GRAPHITE_SERVER** Default: ` ` From 6f2e249e091f2f47bdcb582fcc6c9a5c4d27a05c Mon Sep 17 00:00:00 2001 From: Jan Broer Date: Sat, 6 Feb 2016 08:17:58 +0100 Subject: [PATCH 3/5] Bragging --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 749e1c1..727cba1 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ go-dnsmasq is a light weight (1.2 MB) DNS caching server/forwarder with minimal * Serve static A/AAAA records from a hostsfile * Provide DNS response caching * Replicate the `search` domain treatment not supported by `musl-libc` based Linux distributions +* Supports virtually unlimited number of `search` paths and `nameservers` ([related Kubernetes article](https://github.com/kubernetes/kubernetes/tree/master/cluster/addons/dns#known-issues)) * Configure stubzones (different nameserver for specific domains) * Round-robin of DNS records * Send server metrics to Graphite and StatHat @@ -34,7 +35,7 @@ DNS queries are resolved in the style of the GNU libc resolver: | Flag | Description | Default | Environment vars | | ------------------------------ | ----------------------------------------------------------------------------- | ------------- | -------------------- | | --listen, -l | Address to listen on `host[:port]` | 127.0.0.1:53 | $DNSMASQ_LISTEN | -| --default-resolver, -d | Insert itself into /etc/resolv.conf | False | $DNSMASQ_DEFAULT | +| --default-resolver, -d | Update resolv.conf and make go-dnsmasq the host's primary nameserver | False | $DNSMASQ_DEFAULT | | --nameservers, -n | Comma-separated list of nameservers `host[:port]` | - | $DNSMASQ_SERVERS | | --stubzones, -z | Use different nameservers for specific domains `fqdn[,fqdn]/host[:port]` | - | $DNSMASQ_STUB | | --hostsfile, -f | Full path to a hostsfile | - | $DNSMASQ_HOSTSFILE | From 483198574ae3d92c33b21de5b3c66e7e8e619088 Mon Sep 17 00:00:00 2001 From: Jan Broer Date: Sat, 13 Feb 2016 00:09:06 +0100 Subject: [PATCH 4/5] Use search when name is matching a stub-zone Allow single-label domains for search and stub domain parameters --- main.go | 4 ++-- server/forwarding.go | 24 +++++++++++++++----- server/server.go | 12 ---------- server/stub.go | 53 -------------------------------------------- 4 files changed, 21 insertions(+), 72 deletions(-) delete mode 100644 server/stub.go diff --git a/main.go b/main.go index 52a6740..dd019d9 100644 --- a/main.go +++ b/main.go @@ -186,7 +186,7 @@ func main() { if sd := c.String("search-domains"); sd != "" { for _, domain := range strings.Split(sd, ",") { - if dns.CountLabel(domain) < 2 { + if dns.CountLabel(domain) < 1 { log.Fatalf("This search domain is not a FQDN: %s", domain) } domain = dns.Fqdn(strings.ToLower(domain)) @@ -246,7 +246,7 @@ func main() { } for _, sdomain := range strings.Split(segments[0], ",") { - if dns.CountLabel(sdomain) < 2 { + if dns.CountLabel(sdomain) < 1 { log.Fatalf("This stubzones domain is not a FQDN: %s", sdomain) } sdomain = dns.Fqdn(sdomain) diff --git a/server/forwarding.go b/server/forwarding.go index 378e32d..0a1e087 100644 --- a/server/forwarding.go +++ b/server/forwarding.go @@ -52,6 +52,7 @@ func (s *server) ServeDNSForward(w dns.ResponseWriter, req *dns.Msg) *dns.Msg { var ( r *dns.Msg err error + nsList []string nsIndex int // nameserver list index sdIndex int // search list index sdName string // QNAME with search path @@ -82,11 +83,24 @@ Redo: req.Question[0] = dns.Question{sdName, req.Question[0].Qtype, req.Question[0].Qclass} } + nsList = s.config.Nameservers + + // Check whether the name matches a stub zone + for zone, nss := range *s.config.Stub { + if strings.HasSuffix(req.Question[0].Name, zone) { + nsList = nss + StatsStubForwardCount.Inc(1) + break + } + } + + log.Debugf("Querying nameserver %s question %s", nsList[nsIndex], req.Question[0].Name) + switch tcp { case false: - r, _, err = s.dnsUDPclient.Exchange(req, s.config.Nameservers[nsIndex]) + r, _, err = s.dnsUDPclient.Exchange(req, nsList[nsIndex]) case true: - r, _, err = s.dnsTCPclient.Exchange(req, s.config.Nameservers[nsIndex]) + r, _, err = s.dnsTCPclient.Exchange(req, nsList[nsIndex]) } if err == nil { if canSearch { @@ -113,7 +127,7 @@ Redo: if r.Rcode == dns.RcodeServerFailure || r.Rcode == dns.RcodeRefused { // continue with next available nameserver - if (nsIndex + 1) < len(s.config.Nameservers) { + if (nsIndex + 1) < len(nsList) { nsIndex++ doingSearch = false goto Redo @@ -140,10 +154,10 @@ Redo: w.WriteMsg(r) return r } else { - log.Debugf("Error querying nameserver %s: %q", s.config.Nameservers[nsIndex], err) + log.Debugf("Error querying nameserver %s: %q", nsList[nsIndex], err) // Got an error, this usually means the server did not respond // Continue with next available nameserver - if (nsIndex + 1) < len(s.config.Nameservers) { + if (nsIndex + 1) < len(nsList) { nsIndex++ doingSearch = false goto Redo diff --git a/server/server.go b/server/server.go index 8f9624b..dcbcb00 100644 --- a/server/server.go +++ b/server/server.go @@ -284,18 +284,6 @@ func (s *server) ServeDNS(w dns.ResponseWriter, req *dns.Msg) { return } - // Forward queries matching stub zones - for zone, ns := range *s.config.Stub { - if strings.HasSuffix(name, zone) { - local = false - resp := s.ServeDNSStubForward(w, req, ns) - if resp != nil { - s.rcache.InsertMessage(cache.Key(q, dnssec, tcp), resp) - } - return - } - } - // Forward all other queries local = false resp := s.ServeDNSForward(w, req) diff --git a/server/stub.go b/server/stub.go deleted file mode 100644 index 22e5e53..0000000 --- a/server/stub.go +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) 2014 The SkyDNS Authors. All rights reserved. -// Use of this source code is governed by The MIT License (MIT) that can be -// found in the LICENSE file. - -package server - -import ( - log "github.com/Sirupsen/logrus" - "github.com/miekg/dns" -) - -// ServeDNSStubForward forwards a request to a nameservers and returns the response. -func (s *server) ServeDNSStubForward(w dns.ResponseWriter, req *dns.Msg, ns []string) *dns.Msg { - StatsStubForwardCount.Inc(1) - - tcp := isTCP(w) - - var ( - r *dns.Msg - err error - try int - ) - - // Use request Id for "random" nameserver selection. - nsid := int(req.Id) % len(ns) -Redo: - switch tcp { - case false: - r, _, err = s.dnsUDPclient.Exchange(req, ns[nsid]) - case true: - r, _, err = s.dnsTCPclient.Exchange(req, ns[nsid]) - } - if err == nil { - r.Compress = true - r.Id = req.Id - w.WriteMsg(r) - return r - } - // Seen an error, this can only mean, "server not reached", try again - // but only if we have not exausted our nameservers. - if try < len(ns) { - try++ - nsid = (nsid + 1) % len(ns) - goto Redo - } - - log.Errorf("Failure forwarding stub request %q", err) - m := new(dns.Msg) - m.SetReply(req) - m.SetRcode(req, dns.RcodeServerFailure) - w.WriteMsg(m) - return m -} From ad373966090b9ca03268ba69d3f6d1f83964e99b Mon Sep 17 00:00:00 2001 From: Jan Broer Date: Sat, 13 Feb 2016 00:13:35 +0100 Subject: [PATCH 5/5] Bump v0.9.9 --- main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.go b/main.go index dd019d9..4679694 100644 --- a/main.go +++ b/main.go @@ -27,7 +27,7 @@ import ( ) // var Version string -const Version = "0.9.8" +const Version = "0.9.9" var ( nameservers = []string{}