Skip to content

Instantly share code, notes, and snippets.

@kohenkatz
Created June 14, 2018 03:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kohenkatz/7d896c3d120f9a0c67a8aad64a5de172 to your computer and use it in GitHub Desktop.
Save kohenkatz/7d896c3d120f9a0c67a8aad64a5de172 to your computer and use it in GitHub Desktop.
A nagios `check_dns` alternative that uses dig instead of nslookup
package main
import (
"bytes"
"fmt"
"os"
"os/exec"
"sort"
"strconv"
"strings"
"github.com/simonleung8/flags"
)
func main() {
fc := flags.New()
fc.NewBoolFlag("help", "h", "Print detailed help screen")
fc.NewStringFlag("hostname", "H", "The name or address you want to query")
fc.NewStringFlag("server", "s", "Optional DNS server you want to use for the lookup")
fc.NewStringFlag("querytype", "q", "Optional DNS record query type where TYPE =(A, AAAA, SRV, TXT, MX, ANY)\nThe default query type is 'A' (IPv4 host entry)")
fc.NewStringFlag("expected-address", "a", "Optional IP-ADDRESS you expect the DNS server to return. HOST must end with a dot (.). This option can be repeated multiple times (Returns OK if any value match). If multiple addresses are returned at once, you have to match the whole string of addresses separated with commas (sorted alphabetically).\nIf you would like to test for the presence of a cname, combine with -n param.")
fc.NewBoolFlag("expect-authority", "A", "Optionally expect the DNS server to be authoritative for the lookup")
fc.NewBoolFlag("accept-cname", "n", "Optionally accept cname responses as a valid result to a query\nThe default is to ignore cname responses as part of the result")
fc.NewIntFlag("warning", "w", "Return warning if elapsed time exceeds value. Default off")
fc.NewIntFlag("critical", "c", "Return critical if elapsed time exceeds value. Default off")
fc.NewStringFlag("timeout", "t", "Seconds before connection times out (default: 10)\nOptional \":<timeout state>\" can be a state integer (0,1,2,3) or a state STRING")
err := fc.Parse(os.Args...)
if err != nil {
fmt.Println("UNKNOWN - Parsing error:", err)
os.Exit(3)
}
if fc.IsSet("help") {
fmt.Println(fc.ShowUsage(10))
os.Exit(0)
}
path, err := exec.LookPath("dig")
if err != nil {
fmt.Println("UNKNOWN - Could not find 'dig' command on PATH")
os.Exit(3)
}
if ! fc.IsSet("hostname") {
fmt.Println("CRITICAL - Missing required -H HOST argument")
os.Exit(2)
}
hostname := fc.String("hostname")
q := "A"
if fc.IsSet("querytype") {
q = fc.String("querytype")
}
args := []string{"+short", hostname, "-t", q}
if fc.IsSet("timeout") {
args = append(args, "+time=" + strconv.Itoa(fc.Int("timeout")))
}
if fc.IsSet("server") {
args = append(args, "@" + fc.String("server"))
}
cmd := exec.Command(path, args...)
var out bytes.Buffer
cmd.Stdout = &out
err = cmd.Run()
if err != nil {
fmt.Println("UNKNOWN - Error running dig")
fmt.Println(err)
os.Exit(3)
}
results := strings.Split(out.String(), "\n")
// If there is a blank last element, remove it
if len(results) > 1 && len(results[len(results)-1]) == 0 {
results = results[:len(results)-1]
}
for i := range(results) {
s := results[i]
sz := len(s)
if sz > 0 && s[sz-1] == '.' {
results[i] = s[:sz-1]
}
}
sort.Strings(results)
if fc.IsSet("expected-address") {
expected := strings.Split(fc.String("expected-address"), ",")
if ! Equal(results, expected) {
fmt.Printf("CRITICAL - DNS lookup result different from expected\n")
fmt.Printf("Expected:\t%s\n", fc.String("expected-address"))
fmt.Printf("Got:\t%s\n", strings.Join(results, ","))
os.Exit(2)
}
fmt.Println("OK")
}
fmt.Printf("%v\n", results)
}
func Equal(a, b []string) bool {
if len(a) != len(b) {
return false
}
for i, v := range a {
if v != b[i] {
return false
}
}
return true
}
@kohenkatz
Copy link
Author

See https://github.com/ymkatz/nagios_check_dns_cloudflare for a greatly expanded use of this code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment