diff --git a/check-dns/README.md b/check-dns/README.md index 160d7b3d..b4f87478 100644 --- a/check-dns/README.md +++ b/check-dns/README.md @@ -4,9 +4,45 @@ Monitor DNS response. -## Synopsis +## Usage + +### Options + ``` -check-dns -H example.com -s 8.8.8.8 + -H, --host= The name or address you want to query + -s, --server= DNS server you want to use for the lookup + -p, --port= Port number you want to use (default: 53) + -q, --querytype= DNS record query type (default: A) + --norec Set not recursive mode + -e, --expected-string= IP-ADDRESS string you expect the DNS server to return. If multiple IP-ADDRESS are returned at once, you have to specify whole string +``` + +- query class is always IN. +- Punycode is not supported. + +### Check DNS server status + +If DNS server returns `NOERROR` in status of HEADER, then the checker result becomes `OK`, if not `NOERROR`, then `CRITICAL` + +``` +check-dns -H a.root-servers.net -s 8.8.8.8 +``` + +### Check string DNS server returns + +- The currently supported query types are A, AAAA. + +If DNS server returns 1.1.1.1 and 2.2.2.2 +``` +-e 1.1.1.1 -e 2.2.2.2 -> OK +-e 1.1.1.1 -e 2.2.2.2 -e 3.3.3.3 -> WARNING +-e 1.1.1.1 -> WARNING +-e 1.1.1.1 -e 3.3.3.3 -> WARNING +-e 3.3.3.3 -> CRITICAL +-e 3.3.3.3 -e 4.4.4.4 -e 5.5.5.5 -> CRITICAL +``` +``` +check-dns -H a.root-servers.net -s 8.8.8.8 -e 198.41.0.4 ``` ## Installation @@ -25,7 +61,7 @@ Or you can use this program by installing the official Mackerel package. See [Us Next, you can execute this program :-) ``` -check-dns -H example.com -s 8.8.8.8 +check-dns -H a.root-servers.net -s 8.8.8.8 ``` @@ -34,22 +70,6 @@ check-dns -H example.com -s 8.8.8.8 If there are no problems in the execution result, add a setting in mackerel-agent.conf . ``` -[plugin.checks.fileage-sample] -command = ["check-dns", "-H", "example.com", "-s", "8.8.8.8"] -``` - -## Usage -### Options - -``` - -H, --host= The name or address you want to query - -s, --server= DNS server you want to use for the lookup - -p, --port= Port number you want to use (default: 53) - -q, --querytype= DNS record query type where TYPE =(A, AAAA, SRV, TXT, MX, ANY) (default: A) - -c, --queryclass= DNS record class type where TYPE =(IN, CS, CH, HS, NONE, ANY) (default: IN) - --norec Set not recursive mode +[plugin.checks.dns-sample] +command = ["check-dns", "-H", "a.root-servers.net", "-s", "8.8.8.8"] ``` - -## For more information - -Please execute `check-dns -h` and you can get command line options. diff --git a/check-dns/lib/check_dns.go b/check-dns/lib/check_dns.go index d567cde8..bbc05569 100644 --- a/check-dns/lib/check_dns.go +++ b/check-dns/lib/check_dns.go @@ -13,12 +13,12 @@ import ( ) type dnsOpts struct { - Host string `short:"H" long:"host" required:"true" description:"The name or address you want to query"` - Server string `short:"s" long:"server" description:"DNS server you want to use for the lookup"` - Port int `short:"p" long:"port" default:"53" description:"Port number you want to use"` - QueryType string `short:"q" long:"querytype" default:"A" description:"DNS record query type where TYPE =(A, AAAA, SRV, TXT, MX, ANY)"` - QueryClass string `short:"c" long:"queryclass" default:"IN" description:"DNS record class type where TYPE =(IN, CS, CH, HS, NONE, ANY)"` - Norec bool `long:"norec" description:"Set not recursive mode"` + Host string `short:"H" long:"host" required:"true" description:"The name or address you want to query"` + Server string `short:"s" long:"server" description:"DNS server you want to use for the lookup"` + Port int `short:"p" long:"port" default:"53" description:"Port number you want to use"` + QueryType string `short:"q" long:"querytype" default:"A" description:"DNS record query type"` + Norec bool `long:"norec" description:"Set not recursive mode"` + ExpectedString []string `short:"e" long:"expected-string" description:"IP-ADDRESS string you expect the DNS server to return. If multiple IP-ADDRESS are returned at once, you have to specify whole string"` } // Do the plugin @@ -53,11 +53,7 @@ func (opts *dnsOpts) run() *checkers.Checker { queryType, ok := dns.StringToType[strings.ToUpper(opts.QueryType)] if !ok { - return checkers.Critical(fmt.Sprintf("%s is invalid queryType", opts.QueryType)) - } - queryClass, ok := dns.StringToClass[strings.ToUpper(opts.QueryClass)] - if !ok { - return checkers.Critical(fmt.Sprintf("%s is invalid queryClass", opts.QueryClass)) + return checkers.Critical(fmt.Sprintf("%s is invalid query type", opts.QueryType)) } c := new(dns.Client) @@ -66,7 +62,7 @@ func (opts *dnsOpts) run() *checkers.Checker { RecursionDesired: !opts.Norec, Opcode: dns.OpcodeQuery, }, - Question: []dns.Question{{Name: dns.Fqdn(opts.Host), Qtype: queryType, Qclass: uint16(queryClass)}}, + Question: []dns.Question{{Name: dns.Fqdn(opts.Host), Qtype: queryType, Qclass: dns.StringToClass["IN"]}}, } m.Id = dns.Id() @@ -76,9 +72,58 @@ func (opts *dnsOpts) run() *checkers.Checker { } checkSt := checkers.OK + /** + if DNS server return 1.1.1.1, 2.2.2.2 + 1: -e 1.1.1.1 -e 2.2.2.2 -> OK + 2: -e 1.1.1.1 -e 2.2.2.2 -e 3.3.3.3 -> WARNING + 3: -e 1.1.1.1 -> WARNING + 4: -e 1.1.1.1 -e 3.3.3.3 -> WARNING + 5: -e 3.3.3.3 -> CRITICAL + 6: -e 3.3.3.3 -e 4.4.4.4 -e 5.5.5.5 -> CRITICAL + **/ + if len(opts.ExpectedString) != 0 { + supportedQueryType := map[string]int{"A": 1, "AAAA": 1} + _, ok := supportedQueryType[strings.ToUpper(opts.QueryType)] + if !ok { + return checkers.Critical(fmt.Sprintf("%s is not supported query type. Only A, AAAA is supported for expectation.", opts.QueryType)) + } + match := 0 + for _, expectedString := range opts.ExpectedString { + for _, answer := range r.Answer { + var anserWithoutHeader string + expectMatch := expectedString + switch t := answer.(type) { + case *dns.A: + anserWithoutHeader = t.A.String() + case *dns.AAAA: + anserWithoutHeader = t.AAAA.String() + default: + return checkers.Critical(fmt.Sprintf("%s is not supported query type. Only A, AAAA is supported for expectation.", opts.QueryType)) + } + if anserWithoutHeader == expectMatch { + match += 1 + } + } + } + if match == len(r.Answer) { + if len(opts.ExpectedString) == len(r.Answer) { // case 1 + checkSt = checkers.OK + } else { // case 2 + checkSt = checkers.WARNING + } + } else { + if match > 0 { // case 3,4 + checkSt = checkers.WARNING + } else { // case 5,6 + checkSt = checkers.CRITICAL + } + } + } + if r.MsgHdr.Rcode != dns.RcodeSuccess { checkSt = checkers.CRITICAL } + msg := fmt.Sprintf("HEADER-> %s\n", r.MsgHdr.String()) for _, answer := range r.Answer { msg += fmt.Sprintf("ANSWER-> %s\n", answer) diff --git a/check-dns/lib/check_dns_test.go b/check-dns/lib/check_dns_test.go index 6e3526b4..cf50a987 100644 --- a/check-dns/lib/check_dns_test.go +++ b/check-dns/lib/check_dns_test.go @@ -29,54 +29,79 @@ func TestCheckDns(t *testing.T) { want_msg []string }{ { - []string{"-H", "example.com"}, + []string{"-H", "a.root-servers.net"}, checkers.OK, []string{"status: NOERROR"}, }, { - []string{"-H", "example.com", "--norec"}, + []string{"-H", "a.root-servers.net", "--norec"}, checkers.OK, []string{"status: NOERROR"}, }, { - []string{"-H", "exampleeeee.com"}, + []string{"-H", "a.root-servers.invalid"}, checkers.CRITICAL, []string{"status: NXDOMAIN"}, }, { - []string{"-H", "example.com", "-s", "8.8.8.8"}, + []string{"-H", "a.root-servers.net", "-s", "8.8.8.8"}, checkers.OK, []string{"status: NOERROR"}, }, { - []string{"-H", "exampleeeee.com", "-s", "8.8.8.8"}, + []string{"-H", "a.root-servers.invalid", "-s", "8.8.8.8"}, checkers.CRITICAL, []string{"status: NXDOMAIN"}, }, { - []string{"-H", "example.com", "-s", "8.8.8"}, + []string{"-H", "a.root-servers.net", "-s", "8.8.8"}, checkers.CRITICAL, []string{""}, }, { - []string{"-H", "example.com", "-s", "8.8.8.8", "-q", "AAAA"}, + []string{"-H", "a.root-servers.net", "-s", "8.8.8.8", "-q", "AAAA"}, checkers.OK, []string{"status: NOERROR", "AAAA"}, }, { - []string{"-H", "example.com", "-s", "8.8.8.8", "-q", "AAA"}, + []string{"-H", "a.root-servers.net", "-s", "8.8.8.8", "-q", "AAA"}, checkers.CRITICAL, - []string{"AAA is invalid queryType"}, + []string{"AAA is invalid query type"}, }, { - []string{"-H", "example.com", "-s", "8.8.8.8", "-c", "IN"}, + []string{"-H", "a.root-servers.net", "-s", "8.8.8.8", "-e", "198.41.0.4"}, checkers.OK, - []string{"status: NOERROR"}, + []string{"status: NOERROR", "198.41.0.4"}, + }, + { + []string{"-H", "a.root-servers.net", "-s", "8.8.8.8", "-q", "AAAA", "--expected-string", "2001:503:ba3e::2:30"}, + checkers.OK, + []string{"status: NOERROR", "2001:503:ba3e::2:30"}, + }, + { + []string{"-H", "a.root-servers.net", "-s", "8.8.8.8", "-q", "TXT", "-e", ""}, + checkers.CRITICAL, + []string{"is not supported query type. Only A, AAAA is supported for expectation."}, + }, + { + []string{"-H", "a.root-servers.net", "-s", "8.8.8.8", "-e", "198.41.0.3"}, + checkers.CRITICAL, + []string{"status: NOERROR", "198.41.0.4"}, + }, + { + []string{"-H", "a.root-servers.invalid", "-s", "8.8.8.8", "-e", "198.41.0.4"}, + checkers.CRITICAL, + []string{"status: NXDOMAIN"}, + }, + { + []string{"-H", "a.root-servers.net", "-s", "8.8.8.8", "-e", "198.41.0.3", "-e", "198.41.0.4"}, + checkers.WARNING, + []string{"status: NOERROR", "198.41.0.4"}, }, { - []string{"-H", "example.com", "-s", "8.8.8.8", "-c", "INN"}, + []string{"-H", "a.root-servers.net", "-s", "8.8.8.8", "-e", "198.41.0.3", "-e", " 198.41.0.4 "}, checkers.CRITICAL, - []string{"INN is invalid queryClass"}, + []string{"status: NOERROR", "198.41.0.4"}, }, }